What's difference between let &mut z and let mut z

I was playing around with creating references and I ended up having this:

let mut z: &mut i32 = &mut 42;

but also the following that compiles

let &mut z: &mut i32 = &mut 42;

I was not actually aware that I could have the let &mut z form, as must of the occurrences I have seen has always been let mut z: which I interpret to mean, make it possible for z, to hand out mutable reference.

I am not sure how to interpret let &mut z:.

Variable declarations are patterns, not just names with an optional mut marker. In this case, the &mut in let &mut z "unwraps" the &mut of the type and of the value on the right:

let &mut z: 
    &mut i32 =
    &mut 42

and z ends up being an i32 with a copy of the value 42 stored in it.

Here's a good article on this destructuring pattern.

2 Likes

And it's somewhat tangential, but I'll mention that while &mut T is a distinct type compared to T or &T, the mut marker in a binding (let mut z = ...) doesn't change the type, it just changes whether or not you can overwrite or create a mutable reference to the variable.

It's an aid to avoid mutating things you don't mean to, but you could always assign the value to a new variable with a mutable binding, so adding or not adding mut doesn't change anything inherent to the type or value. It's just a property of the binding.

For example you can do:

let z = 13;
let mut z = z;
println!("{z}");

z = 42;
let z = z;
println!("{z}");

(And every z is an i32.)


Tying it back to the example, this fails:

    let &mut z: &mut i32 = &mut 42;
    z = 13;}

But this works:

    let &mut mut z: &mut i32 = &mut 42;
    //       ^^^
    z = 13;

(But naturally you would just write let mut z = 42; in more realistic code.)

1 Like

But this also works

    let  mut z:
        &mut i32 =
        &mut 42;
   
   dbg!(z) // prints 42

and z ends up being an i32 with a copy of value 42 stored in it. So still not sure why that should work since it should be a wrong pattern? Or not?

No, with let mut z: &mut i32 = &mut 42, z is a &mut i32 not an i32. A &mut i32 prints out the value referenced, so they display the same thing when passed to println!.

The mut in let mut z is the mutable binding marker, and doesn't "unwrap" anything on the right. It's not a part of any types. It's not &mut in particular.

You could rewrite the snippet like so.

let z: &mut i32 = &mut 42;
let mut z = z;
dbg!(z);
1 Like

In your initial response you said

Variable declarations are patterns, not just names with an optional mut marker. In this case, the &mut in let &mut z "unwraps" the &mut of the type and of the value on the right:

When are variable declarations patterns? Like in the case of let &mut z: and when are they not patterns? As in the case let mut z, where the mut is just a mutable binding marker, and doesn't "unwrap" anything

Well, variable declarations are always patterns. Really you're asking the very sensible question, when is mut just a marker?

mut is just a marker when it does not follow & or ref. It is also followed by the name of the variable you're binding. More generally, the mut marker is part of an identifier pattern.


This does actually lead to ambiguity in some places. For example, how can you write a single pattern for this?

let &x = &42;   // x is an i32
let mut x = x;

This won't work -- it tries to match against a &mut i32 on the right.

let & mut x = &42;

This works... but I don't think I've ever seen such a declaration in real code and don't really recommend it.

let &(mut x) = &42;
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.