Why is it "mut foo: Type" instead of "foo: mut Type"?

This threw me for a loop first time I encountered it.

I would have expected:

fn bar(foo: mut Type) {
}

Since it's:

fn bar(foo: &mut Type) {
}

But instead it's:

fn bar(mut foo: Type) {
}

I guess it makes more sense if one considers:

let mut foo: Type = {};

While typing this I'm less confused than I was when I originally encountered it -- still, would be nice if there's a "It couldn't be the other way, because <reason>" explanation.

They just have different semantics, I think.
When you write mut foo: Type, foo can bind to an object of Type in function.
With foo: &mut Type, you cannot.

1 Like

We could say that in Rust "everything is mutable", in that no matter the type, you could have a mutable "instance" / element of that type.

The choice Rust has made in that regard is to reason about mutability, not w.r.t. types, but w.r.t. particular instances / elements of that type. And actually it's something even more precise than that: the mutability applies to a binding, i.e. to a "variable name".

Consider the following example:

fn foo (s: String) // looks like s is immutable
  -> String
{
    let mut s2 = s; // change the name of the variable, and "enable" mutability under this new name
    s2.push_str(" [mutated!]");
    /* return */ s2
}
  • An oversimplification to express this would be to say that "ownership implies mutability".

Now, you mention the &mut ... type, and in the other direction, we could mention types such as &str which would be "immutable".

The thing is, this confusion comes from being wave-handed regarding the definition of what is mutable.

Indeed, a variable / a type may be referring / pointing to some other place in memory. And Rust does encode the kind of access this pointer has:

  • owned (and usually exclusive), such as: Box<T>,

  • borrowed, but in an exclusive manner, such as &mut T,

  • borrowed, but in a shared manner, such as &T.

And for the borrowed, but in an exclusive manner, Rust allows us to mutate the pointee, hence (abusingly) calling it a "mutable reference".


Here is an example showcasing the difference between mut p: &i32 and p: &mut i32:

let mut x = 42;
{
    let p = &mut x; // we get an exclusive borrow over `x` contents,
    // we can thus mutate those:
    *p = 0;
    assert_eq!(*p, 0);
}
assert_eq!(x, 0);

vs.

let x = 42;
let z = 0;
{
    let mut p = &x; // shared borrow over `x` contents.
    // Since it is shared, here the contents become immutable
    // for the duration of the borrow.
    // *p = 0; <- Error: we can't mutate the pointee
    p = &z; // but `mut p` allows us to change `p` itself,
            // _i.e._ we can change our pointer so that
            // it points somewhere else:
    assert_eq!(*p, 0);
}
assert_eq!(x, 42); // unchanged!

See this blog post about Rust mutation: To mut or not to mut.

5 Likes

You can also have

With a reference, the reference itself may or may not be mut, and you may or may not be able to mutate the thing it is referencing. So you actually have four possibilities:

x: & Type
mut x: Type
x: &mut Type
mut x: &mut Type
2 Likes

Here's a couple of vaguely practical examples to show the difference. In the first case, I have a reference to a mutable buffer and can modify its contents:

fn set_last_byte_null(buf: &mut [u8]) {
    if let Some(i) = buf.last_mut() {
        *i = 0;
    }
}

But in this second case, I have a mutable reference to a buffer and can shift the reference around, but not modify the buffer itself:

fn strip_null_bytes(mut buf: &[u8]) -> &[u8] {
    while let Some((0, rest)) = buf.split_last() {
        buf = rest;
    }
    buf
}
2 Likes

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.