Allow mut value, not just mut reference

Hello,

I would like to propose the following way of declaring variables.

let a = mut true;

and

let a: mut bool = true;

would all act same as:

let mut a = true;

Similar and parallel to how references are declared.

let a = &mut S{};

Comparison between &mut and mut

&mut placement:

  • Can come as let pattern: let ref mut a = ..
  • Can come as type pattern: ..:&mut ..
  • Can come as value expression: ..=&mut ..

mut placement:

  • Can come as let pattern: let mut a = ..
  • Can come as type pattern: ?
  • Can come as value expression: ?

What’s your opinion?

Thank You,

For what purpose? What’s wrong with “let mut a = true”? Why have redundant syntax options?

2 Likes

why not? you can do:

 let mut a = S{}

and

 let a = &mut S{}

Both are equivalent (Although one is value and other is a reference).

You can also do let mut a = &mut x; which would mean you could later change a to be a mutable reference to another variable.

A mut after let means the variable can be changed. This is not equivalent to a mut at any place right of the =.

5 Likes

Actually, they are not equivalent at all.

let mut a = s{}

defines a local variable a that has a non-mutable owned value s{} assigned to it. Because the “binding” a is mutable, you can re-assign a different owned value to a later in the scope.

let a = &mut s{}

Creates a binding a that cannot be rebound in the local scope to a mutable reference to an s (meaning that you are able to mutate the s value through the a variable).

Not the same thing in any way, shape, or form.

3 Likes

I already said one is a value while the other is a reference.

Yes, but, that is not the important point. You tried to claim they were equivalent in what you can mutate. They are not.

3 Likes

I think this is a misunderstanding about mutability in Rust. In Rust, values don’t have mutability, but name bindings and referred lvalues have. This is why you attach the mut to a binding, or indicate that you request a &mut reference to an lvalue.

You should think of name bindings/variables as boxes, and the bound value as an object in the box. Whether you can replace the contained object in the box doesn’t depend on the contained object, but on the box itself. Therefore, it doesn’t really make sense to speak of “mutable values”, let alone to add syntax for them.

Even if it made sense, I would oppose adding another, redundant syntactic construct for the same semantics. There has been a similar request a couple of days ago, whereby someone proposed the alternative var spelling instead of let mut. For the same reasons I’ve enlisted there, I still stand by my point: it’s a bad idea to have two different-looking things do an identical thing, because it’s confusing.

1 Like

Just a question: Is there a way in rust to cast &mut into a value, so you can use the variable directly instead of pointers?

For example:

 let a = &mut true as .....?;
 a = false;

instead of doing:

 let a = &mut true;
 *(a) = false;

I value flexibility in a language.

let mut a = true;
...
a = false;

That’s what “let mut a” is saying. It is saying, “I can reassign a new value to this variable a later in this scope”.

2 Likes

What about ref keyword? It’s synonyms with & to a great degree.

There was a rejected RFC to add a built-in API for that idiom: RFC: `core::mem::replace_with` for temporarily moving out of ownership. by ticki · Pull Request #1736 · rust-lang/rfcs · GitHub

1 Like

And that’s bad enough in itself. E.g. I strongly dislike writing ref-patterns where & would do, so I prefer

let ptr = &something;

over

let ref ptr = something;

However, ref patterns are inevitable in some situations, for example when pattern matching on a reference-to-enum:

let e = &Some(move_only_value);
match *e {
    Some(ref ref_to_move_only) => …,
    None => …,
}

So, because it is basically necessary, ref was added as a pattern component, but then for completeness and generality, it was allowed to (not restricted from) being used in let bindings, which are just regular patterns anyway.

1 Like

That’s not “flexibility”, that’s a plain old type error. If you have a reference, why do you want to assign a bool to it? Magic implicit conversions don’t do any good except in the most limited cases, and this is not one of them. There would be no value in this, it would merely allow some erroneous code to slip through type checking. That sharply opposes Rust’s goals and overall design philosophy.

Dereferencing a pointer literally requires one character of typing, the prefix * operator. I simply do not buy that it’s so much of a burden that it warrants removing a useful type system restriction from the language altogether.

5 Likes

Since there are special uses for the ref keyword, and usage difference between &mut and let mut, I am sure when = (mut) val is implemented, there will be special situations where it’s necessary for code to be written in this form.

Yes, and ref is so confusing and misleading that we got the match ergonomics RFC, which idiomatically kills ref. (Of course it's still allowed for compat, but I'd pretty much teach it as "don't use it ever".)

4 Likes

More examples how mutability of the binding is different from mutability of the reference:

let a = &mut S{v:1};
a = &mut S{v:2}; // error

let mut b = &mut S{v:1};
b = &mut S{v:2}; // ok

let a2 = S{v:1};
a2 = S{v:2}; // error

let mut b = S{v:1};
b = S{v:2}; // ok

and

let a = S{};
let mut a = a; // ok!

let a = &S{};
let a = &mut a; // error, different meaning
let a = &mut *a; // error

ref/& mix-up is unfortunate, and got me quite confused when I was learning Rust.

Perhaps the patterns should have used * instead of & to mean dereference as an action rather than match of a reference:

let a = &Some(1);
match a {
  &Some(ref x) => {}
}

should have been

let a = &Some(1);
match a {
  *Some(&x) => {}
}

But I’m not here to create a new keyword, I’m just proposing to allow place flexibility of mut. It can come after let, and after assignment.

Right now, mut is allowed in patterns but not expressions. You want to add it to expressions… to do what? What does it mean there? It’s hard to imagine what that would mean in arbitrary expressions. What does let a = (mut a + b) mean, for instance?

Or do you mean to allow it only in the outermost part of expressions after a let statment?

1 Like