Suggestion: "mut", like "let" keyword


#1

I would like to propose a mut keyword that simply provides visual balance to the let keyword.

E.g.

let x = 6
mut thing.value = x

The idea is purely to provide visual balance and make it clear when something mutable occurs; this should act as a reminder to new users that they need to define a variable as mutable in order to mutate it.

It also helps to make it clearer when you’re about to make the mistake of trying to modify a non-mutable reference.


#2

“mut” is an attribute that occurs in NINE different productions of the Rust grammar. It is not a verb. I’m sure some of the Rust language team can explain this further.


#3

Also, I feel like this would cause people to be more likely to use mut by default, which IMHO would be bad.


#5

They are suggesting an optional “mut” at the start of an arbitrary assignment - not bindings.

E.g.

let foo = 1;
let bar = || foo;
mut foo = 2;
assert!(bar() == 2);

#6

Correct, this is not about bindings. I am not aware of the details of the whole language so I can’t say if this wouldn’t fall apart in practice, but just as the “dyn” keyword has been forced on Trait objects, purely for a heads-up warning to the developer “WARNING: THIS ISN’T STATIC!”, I’d like a “mut” keyword so that as you scan the code you can see “modification happens here”.

If new users do take to writing it by default, then it only helps when the compiler spits out the warning “you are trying to mutate a thing here, but it’s not defined as mutable --> here”.

IMO

let x = 6
mut thing.value = x

looks better than

let x = 6
thing.value = x

especially when scanning code quickly, it allows you to identify the assignments out from function calls etc.


#7

That’s just the same as looking for every instance of =, right?


#8

how about:

let foo;

foo = bar;

#9

In the Rust grammar, let occurs in condition sub-expressions within if and while expressions, as well as in one of the four alternative productions for statements.

In my experience, relatively few = assignment statements are instances of delayed initialization of immutable variables. If that’s also the case for the OP, they could handle those cases by annotating only those assignment statements:

let foo;
\*!mut*\ foo = bar;

All other assignment statements [added: without a let prefix] in the program would be presumed to be assignments or reassignments to mut variables.

For the record, here are the current instances of the “mut” keyword in the productions of the draft grammar for Rust that is being developed by the grammar-wg. (In the following, means that the production contains other alternatives that are not shown.)

Expr = attrs:OuterAttr* kind:ExprKind;
ExprKind = …
    | Borrow:{ "&" mutable:"mut"? expr:Expr }

Item = attrs:OuterAttr* vis:Vis? kind:ItemKind;
ItemKind = …
    | Static:{ "static" mutable:"mut"? name:IDENT ":" ty:Type "=" value:Expr ";" }

ForeignItem = attrs:OuterAttr* vis:Vis? kind:ForeignItemKind;
ForeignItemKind = …
    | Static:{ "static" mutable:"mut"? name:IDENT ":" ty:Type ";" }

FnArg = …
    | SelfValue:{ mutable:"mut"? "self" }
    | SelfRef:{ "&" lt:LIFETIME? mutable:"mut"? "self" }

Pat = …
    | Ref:{ "&" mutable:"mut"? pat:Pat }

Binding = boxed:"box"? reference:"ref"? mutable:"mut"? name:IDENT;

Type = …
    | RawPtr:{ "*" { "const" | mutable:"mut" } pointee:Type }
    | Ref:{ "&" lt:LIFETIME? mutable:"mut"? pointee:Type }

#10
let x = 6;
{thing}.value = x;

This works already in Rust today. Problem solved :wink:


#11

How curious :S To those saying “you can already do this”; that’s like saying that try! already exists, there was no need for ?.

That’s not helping.

Also, the reason why an RFC of this would likely die on the first comment :confused:


#12

The cognitive dissonance here is that let is a keyword that results in binding a type and/or an evaluated expression to an identifier. mut is simply an attribute of that binding, conceptually no different than & or static or const or ref or a lifetime 'a.

IIUC, the OP’s proposal is to prefix that one attribute to assignment statements when the target of the assignment has that attribute. Should the same promotion of attributes to the beginning of statements occur for other attributes? Would it make sense to prefix assignment statements for references with &?


#13

It works quite strange Please share some URL where I can read about this {variable}.key syntax - can’t find it using google.


#14

This is just a normal block { /* code */ }, so {thing} moves thing, then .value accesses the place on that temporary, which is then = x assigned and ; then tossed.

Using thing afterwards will show that it’s been moved.

You’ll somewhat rarely see {x} used to enforce a move on x rather than treating it as a place. (Personally, I typically end up creating a fn coerce<T>(t: T) -> { t } that I use to enforce moves. It’s nice for being able to write e.g. coerce::<&str>(&String) as well since type ascription in expressions isn’t anywhere near stable yet.)


#15

Oh, right. I missed that.


#16

Sorry, I can’t understand this part yet. What exactly we access if braces here are just normal block? Right now for me it’s the same as write { let x = 5; println!("{}", x); }.some_field - what is the difference?


#17

Nothing. It’s literally just a block. The value of the block is the value of the final expression. So if the block only has one expression, then that is the value. Therefore the value of {x} is just the value of x itself.


#18

The difference between x.value = and {x}.value = is that x is a place expression whereas {x} is a value expression. See https://doc.rust-lang.org/1.30.0/reference/expressions.html#place-expressions-and-value-expressions for a detailed explanation.


#19

thanks, now I get it


#20
let mut k;
loop {
    k = fetch_data(); // should this get a `mut` prefix?
    if good(k) { break };
}

#21

I’d rather use something like “mutable accessor” for mutating fields:

let x = 6;
thing~value = x;

And for calling functions which takes &mut self:

thing
    ~mutate()
    ~mutate_again()
    .move_it();

If you interested, more use cases and syntax variations could be found in this thread.