In the following code from rustlngs, we are able to change self without making self as mutable. I wonder why this is even allowed by Rust compiler:
trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
// compiler allow us to change the value of self, even the parameter is not "mut self"
fn append_bar(self) -> Self {
self + "Bar"
}
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {s}");
}
Without mut on a binding, you can't overwrite the variable (once initialized) and you can't take a &mut _ to the variable. That's pretty much it. You can still move it -- including moving it to a new binding which does have mut. That's what's going on here (over a function boundary in this case, but you could inline the operation too).
Incidentally Rust also has shared mutability and you can also mutate through a x: &mut T without putting mut on x itself.
If we consider this code as valid. The following code must be valid as well. Since String and Vec are both on heap not on heap. Hence the behaviour of both of them must be the same:
Let's simplify the example by removing traits and Strings and just deal with a regular function operating on i32:
fn next_number_a(x: i32) -> i32 {
x + 1
}
fn next_number_b(mut x: i32) -> i32 {
x += 1;
x
}
In next_number_a: x never changes, so it doesn't need to be marked mut.
In next_number_b: x changes, so it needs to be marked mut.
It's the same reason that mut is required in one of your implementations and not the other.
Note that mut on a parameter does not affect function signature. It doesn't matter to callers, it is an internal implementation detail of the function.