Infix notation for macros?

To illustrate with an example, I would love to see the ability to write something like:

  let r = some_operation_returning_a_result().expect_format!("An unknown error occured doing something: {:?}")

Which would roughly transform into:

  let r = some_operation_returning_a_result().map_err( |e| panic!("An unknown error occured doing something: {:?}", e))?

Note the infix notation for the macro expect_format!, whose input would be the expression it was called on. This notation would be more fluently readable than what can be done currently:

  expect_format!(some_operation_returning_a_result(), "An unknown error occured doing something: {:?}");

There is an open RFC for this: https://github.com/rust-lang/rfcs/pull/2442

10 Likes

Terminological note: that's not "infix", that's "postfix", because it acts as a postfix operator with a unique left-to-right precedence. Infix macros would mean something like

let r = some_operation_returning_a_result() expect_format! "An unknown error occured doing something: {:?}";

Note the absence of dot operator and the brackets, which mean that now you would also need to deal with precedence of various infix macros, as well as other infix operators (+, -, *, /, %). That basically moves into the "arbitrary operator symbols" territory, which Rust prefers to avoid.

How is it postfix if it comes between two of its arguments ("receiver" and the rest)? Wouldn't postfix means that it comes after both like RPN?

I would argue this is neither infix nor postfix because it is not represented as a single symbol. The final parenthesis is part of the operator but it's after the second operand, separated from the rest of the operator.

4 Likes

The function call operator is an odd duck. I can think of at least three different ways to model it, but for purposes of macros in method-call position, I think it makes sense to call it "postfix" because it's postfix relative to the method receiver and that may (or may not, depending on how we decide method-like macro dispatch works -- I have no opinion there) affect which macro is being invoked.

The arguments (if any) within the parentheses don't affect dispatch (at least, I haven't seen anyone proposing that for Rust) and you could describe funcall as "merely" passing them through to the callee, rather than actually operating on them.

8 Likes

To be clear, they don't (wouldn't) affect macro dispatch (insofar as the pattern matching within a macro is not called "dispatch", anyway). They definitely affect method dispatch because Rust has a form of method overloading based on trait impls. Edit: oops, ignore this.

Method dispatch does not consider the types of the (non-Self) arguments

error[E0034]: multiple applicable items in scope
8 |     ().foo(5i32);
  |        ^^^ multiple `foo` found
note: candidate #1 is defined in an impl of the trait `Foo1` for the type `()`
1 | trait Foo1 { fn foo(&self, arg: i32) { } }
note: candidate #2 is defined in an impl of the trait `Foo2` for the type `()`
4 | trait Foo2 { fn foo(&self, arg: &str) { } }

(EDIT: You can implement method overloads by having an extra dispatching function that pushes all types into the Self type through an extra trait though, quick overcomplicated playground, real usecases can normally be done much simpler).

2 Likes

If it weren't for the fact that it's a bad idea from a readability point of view (and because the parser chokes on at least some unicode characters), you could almost use this for infix math operations:

a.⋐!(b)
2 Likes

Rust uses the standard definition of what characters are allowed in identifiers, UAX#31 Unicode Identifier and Pattern Syntax (specifically, UAX31-R1-2, although I don't recall the exact profile atm; it's the obvious one to adjust the ASCII character set).

I actually do agree in principle that mathematical symbols should be considered in UAX31 (probably by a new property like Pattern_Syntax but extensible like the XID sets), but that's a question for the Unicode Consortium to hash out. Rust rightly just defers to Unicode's definition of what a source code identifier should look like rather than making its own definition.

3 Likes

Thank you, I didn't know that Unicode had a standard for identifiers. And as much I hate it, I think you're right that Rust needs to stick to the standard here.

1 Like