Disallow using assignment expression a = b directly inside a function call.
let mut a = 1;
f(a = 2); //~ ERROR: Assignment expression cannot be used as function parameter
Rationale
Reserve the f(a=b) syntax for named argument.
Chosen because we already have println!("{a}", a = 2);
a = b returns () which is usually useless as a function parameter. For instance, the above example can be written more clearly as
let mut a = 1;
a = 2;
f(());
One could still wrap the expression in brackets like f((a = b)) or f({a = b}) if one really wants to put an assignment expression there
Detailed design
In Rust 2015, emit a future-incompatible warning during parsing (an error in Rust 2018), when an assignment expression is encountered parsing a call expression.
Wrapping the assignment expression in a bracket should be allowed:
function((a = 2), { b = 3 }); // all allowed
Using assignment expression besides function calls should still be allowed:
let b = [a = 2]; // allowed
let c = (a = 3,); // allowed
let mut d = || a = 4; // allowed
e[a = 5] = 6; // allowed
let f = Struct { g: a = 7 }; // allowed
// Note that anything of the form `x(a=b)` is still disallowed, like
let h = Some(a = 8); // disallowed
Compound assignment is not affected
f(a += 1); // allowed
Macro variables expanded via $($x:tt)* is disallowed
We may want the syntax f(a: b) for named argument, which means we should disallow type ascription too.
Unlike assignment, I expect the result of type ascription being much more useful, and thus there is much higher chance seeing this inside a function call. OTOH type ascription is still unstable, so we could change the type ascription syntax instead.
Do nothing
This means named argument cannot consider f(a = b) as a possible syntax, though alternatives syntax e.g. f(a => b) are still available.
Note
This Pre-RFC is mainly about reserving syntax which will conflict with named argument in the old edition.
I know named argument is a hot topic, but please avoid discussing how to declare a named argument function or whether f(a=b, b=a) and f(b=a, a=b) are equivalent here, which are irrelevant to this Pre-RFC.
It would be nice to warn/reserve all the potential syntaxes for named arguments: :, =>, :=, etc.
Well, neither of the syntaxes you proposed are valid rust code (edit: in that context) at the moment, so there is no need to reserve those! Instead the = one is currently valid.
@H2CO3 For me it’d make more sense if struct construction used the = as well. (This idea is not from me. I’ve heard this somewhere) So: let my_struct = MyStruct { a = 7 } instead of MyStruct { a: 7 }. Then : would always refer to types and = to assignment.
I am used to it, I’ve been programming in C for 9 years, and C has the same syntax (except that it also requires a leading . before field names). I’m still glad Rust didn’t make that choice.
I’ve been working with JavaScript for just as long. It uses the : as well. This doesn’t mean that it makes sense though. It’s an assignment.
In any case, you’re right with one thing: Struct construction and the syntax for named params (if we ever get them) should use the same notation. Be it : or =.
You can look at it as an assignment. But you can also look at it as a name-value pair (which it is), and in that case it makes perfect sense. Spelling both as : also has the additional advantage of requiring one less breaking change.
MyStruct { a: 7 } could just be deprecated very gently (no breaking change) and rustfix could update existing code very quickly to MyStruct { a = 7 }. It'd make usage of type ascription (e.g. MyStruct { a = 7: u64 } in struct construction possible. Not bad if you ask me. (Edit: Although it's probably worthless because the struct is already typed :D)
That said, this RFC has minimal impact and it keeps the door open for both ways. Whatever the choice I'd prefer consistency between these two notations.
I wasn’t even aware this was possible and can’t really see any use for it so I would support disallowing it, as well as in struct if it’s currently allowed. I doubt that anyone is using that “feature” on purpose?!
@Keatsa = b is an expression like any other. That’s why you can use it in situations like
match expr {
pat => a = b,
}
It’s just that, since it has type (), it’s not a very useful expression. Banning it in certain situations would complicate Rust’s syntax though, it could break macros for instance.
This makes me wonder whether a = b should be an expression at all. Maybe match arms like that should require a block to be written:
match expr {
pat => { a = b; },
}
Alternatively, make match the special case and have an extra syntax rule for => a = b (are there any other places where assignment-as-an-expression is useful?).
Edit: Also +1 to changing the struct expression syntax to MyStruct { x = y } in the new edition.
Changing the type ascription syntax feels like a bad type of judgement
Personally I hope we don't; but I have nothing against future proofing
If y'all want to change the struct initialization syntax to field = expr or at least not allow field: value and field: binding, then you need to at minimum crater run this to see the extent of the breakage. My hunch is that the breakage extent makes it wildly undoable. I think nearly every crate would be broken by this change or start emitting warnings (depending on strategy).