(This topic may be better suited to URLO.)
Note that you cannot assign more than once even if no value is overwritten -- even if you're reinitializing a variable which was initialized at some point prior.
let mut s = String::from("hi");
let _move_out_of_s = s;
// The compiler will not let you use `s` here as its value has been
// moved. `s` is effectively no longer initialized.
// println!("{s}"); // error[E0382]: borrow of moved value: `s`
// This line is only allowed because `s` was declared with `mut`.
s = String::from("hello");
// If you remove the `mut` you get an error instead:
// error[E0384]: cannot assign twice to immutable variable `s`
// Now `s` is usable again.
println!("{s}");
Sometimes it's possible to use variable shadowing instead of mut.
let s = String::from("hi");
let _move_out_of_s = s;
// Here we introduce a new variable with the same name.
let s = String::from("hello");
println!("{s}");
// You can shadow variables even if they're still initialized and
// even if the new variable has a different type.
let s = 42;
println!("{s}");
Not having mut doesn't prevent moving values, including to a new mut variable.
let s = String::from("hi");
let mut s = s;
s.push('!');
println!("{s}");
In short, the new things declaring a binding (variable) as mut allows you to are
- assigning more than once
- including overwriting values and reinitializing the variable
- creating a
&mut _ to the variable
But you can initialize a variable once, move it, shadow it, and take a &_ to it, all without mut.
Incidentally, thoughout this topic and throughout most of the book, an "immutable vs mutable" terminology has been used. But Rust's model is actually more "shared vs exclusive". For example, late in the book, they reveal that sometimes values can be mutated behind &_ references.
So you may wish to consider more of a shared vs exclusive perspective. I wish I had been introduced to the concept earlier in my Rust learning as it would have avoided a lot of seeming incongruity and relearning.
E.g. the following compiles, as none the mut x abilities (&mut x, reassignment) are required.
let x = Cell::new(0);
assert_eq!(0, x.get());
x.set(1);
assert_eq!(1, x.get());
(This example is contrived, but you'll inevitably run into shared mutability at some point, as it underlies shared ownership, the multithreaded primitives, and many interactions with the operating system.)