What does mut mean exactly?

In C/C++, the semantic about pointer and const is very clear:

int i = 10;
const int * const p = *i;

but in Rust, I am still confused about the semantic of reference and mut. For example,
let b = &mut Box::new(5);
let mut b = Box::new(5);
let mut b = &mut Box::new(5);

// No compile error.
fn main() {
let mut b = Box::new(5);
*b = 9;
println!("b = {}", b);
}

fn main() {
let b = &mut Box::new(5);
*b = 9; // error[E0308]: mismatched types
println!("b = {}", b);
}

This creates a pointer to a pointer, where both pointers guarantee single ownership so you can mutate it.

*b = 5;

doesn't work because *b is of type Box<i32>, and you can't assign an i32 to a Box<i32>.

You can however

*b = Box::new(6);

which assigns a Box<i32> to itself, or

**b = 6;

which assigns an i32 to an i32.

2 Likes

The rules of writing types and expressions in Rust are much simpler than in C++. I don't really know what exactly confuses you in the code you provided. It would help if you asked more specific questions.

Regarding mut, it's important to know that it has two distinct, not really connected meanings. First, it's a part of the exclusive reference syntax: &mut target (as opposed to shared reference syntax &target). This can be used both in types (e.g. &mut Box<u32> type is an exclusive reference to Box<u32>) and in expressions (e.g. &mut Box::new(5) expression is an exclusive reference to Box::new(5)). Forgetting to take a reference to something usually results in type errors, so the compiler messages should help you with that.

The second meaning of mut is that it marks a binding as mutable. For example, it can appear in this role in a local variable declaration:

let mut b = 5;

Or in a pattern:

if let Some(mut x) = something { /*...*/ }

Or in an argument declaration:

fn function_name(mut arg_name: u32) { /*...*/ }

Please note that in the second meaning, mut has no impact on type of the binding or values of any expressions. It only allows you to create a mutable reference to the value of the binding, which is not allowed otherwise. So you may or may not need to declare a variable with mut, depending on how you use the variable later. To make it easy, you can just make a binding mut when the compiler asks you to, and remove mut when the compiler complains about it (note that both of these errors/warnings are not type errors).

6 Likes

The Box owns the contents in this case. Compare:

#[derive(Debug)]
struct Container {
  number: isize,
}

fn main() {
  let mut c = Container { number: 5 };
  c.number = 9;
  println!("{:?}", c);
}

Run on the playground

The main difference in this example is the use of field access (c.number). Box acts as a smart pointer; along with other things, Box implements the DerefMut trait. This is what allows *b = 9 to function -- a &mut isize is coerced and dereferenced to allow the assignment.

Extending the example:

use core::ops::{Deref, DerefMut};

#[derive(Debug)]
struct Container {
    number: isize,
}

impl Deref for Container {
    type Target = isize;
    fn deref(&self) -> &Self::Target {
        &self.number
    }
}

impl DerefMut for Container {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.number
    }
}

fn main() {
    let mut c = Container { number: 5 };
    *c = 9;
    println!("{:?}", c);
}

Run on playground

(Note that Box also stores its contents on the heap and does other things; the example is just to illustrate DerefMut.)

1 Like

Is it? C has pretty much the same distinction:

int *i;
const int *i;
int const *i;
int *const i;
int const *const i;

Attention check: two of those lines mean the same thing! Which ones?

In C const is a qualifier that can be applied to any type. But the way constness works is different between pointer types and non-pointer types. constness doesn't follow non-pointers the way it does pointers; you can pass a const int to a function expecting int, but you can't pass a const int * to a function expecting int * (not without a cast, anyway). It's almost like const has two meanings: one for const-qualified pointers, and one for named variables...

... Which is of course exactly how mut works in Rust, as others in the thread have already explained.

2 Likes

Thanks @Riateche.

  1. What does "&mut" ("&" and "mut" separately) in the code below mean?
    Does it make sense to use &mut in the operator ='s left side?

let &mut b = 5;

  1. Why the code below is wrong?
fn main() {
    let a = 3;
    let mut b = &a;
    *b = 9; // error[E0594]: cannot assign to `*b` which is behind a `&` reference
}

Sorry for asking a maybe incorrect question, what is the difference between the C code below?

void main() {
    int a = 3;
    int* b = &a;
    *b = 9;
}
  1. So the mut in the operator='s left side means, (in a C style description), p and *p are both mutable, am I right?
fn main() {
    let mut p : Box<i32> = Box::new(3);
    p = Box::new(4);
    *p = 5;
    println!("{}", a);
    println!("{}", *a);
}

First, note that this does not compile. The error:

error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     let &mut b = 5;
  |         ^^^^^^ expected integer, found `&mut _`
  |
  = note:           expected type `{integer}`
          found mutable reference `&mut _`

So, in this case Rust expects the right side to be some king of exclusive reference. The left side in this case is called a pattern: it describes the shape of the thing we want to use, and binds the variables to the values inside. For example, this will work:

fn main() {
    let &mut b = &mut 5;
    println!("{}", b);
}

In this case, we're saying the following: "we are putting here an exclusive reference to something, please bind the value behind this reference to variable b". So, b is bound to the value 5.

1 Like

Thanks @Cerber-Ursi
So basically the two lines of code below are the exactlay same from the compiler's point of view?

 let &mut b = &mut 5;

 let  b: &mut i32 = &mut 5;

Not quite. In the first case, b is of type i32, not &mut i32, since it is bound to the value behind the reference, not to the reference itself.

As for this question:

fn main() {
    let a = 3;
    let mut b = &a;
    *b = 9; // error[E0594]: cannot assign to `*b` which is behind a `&` reference
}

Here, b is assigned an immutable reference as value. An immutable reference allows no mutation through it, so you cannot mutate a through b. The mut on b is for something else — it allows you to change where the pointer points to, but not the contents behind the pointer.

fn main() {
    let a = 3;
    let b = 4;
    let mut c = &a;
    println!("{}", c); // prints 3
    c = &b;
    println!("{}", c); // prints 4
}

playground

1 Like

To add to that, the non-transitive property of const in C makes all sorts of constructs invalid that """should be""" valid intuitively. For instance, there's the famous problem whereby you can't pass your char **argv to a function expecting const char **.

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.