I know how to make code work I'm just wondering why is it so.
Suppose the following program:
fn dummy(name: String) {
let last_name = " Wang".to_string();
name.push_str(&last_name);
println!("Hello, {}", name);
}
fn main() {
println!("What is your name?");
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Couldn't read input!");
name.pop();
dummy(name);
}
When trying to compile it the following error occurs:
error[E0596]: cannot borrow `name` as mutable, as it is not declared as mutable
--> print.rs:3:5
|
1 | fn dummy(name: String) {
| ---- help: consider changing this to be mutable: `mut name`
2 | let last_name = " Wang".to_string();
3 | name.push_str(&last_name);
| ^^^^ cannot borrow as mutable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0596`.
I know that just adding mut
next to name
in function definition solves this problem, but why need for declaring it as mutable in function definition when variable name
was defined as mutable previously inside of main
function?
Shouldn't the compiler know that variable was mutable before and why can't it transfer ownership and mutable "property" along with it?
Maybe this is stupid question but I'm new to Rust. If it behaved so would it introduce possibility for some new problems/bugs? If yes, can you give some examples?
The fact that name
is passed as a parameter is just a detail.
In this simplified example, we can reproduce the same effect.
fn main() {
let mut name1 = "first".to_owned();
name1.push_str(" second");
let mut name2 = name1;
name2.push_str(" third");
let name3 = name2;
// name3.push_str(" fourth"); // rejected
let mut name4 = name3;
name4.push_str(" fifth");
println!("{}", name4);
}
Ownership of the string changes from name1
to name2
, name3
then name4
and each of these variables (bindings) decide (with or without mut
) if the string, of which it is now the sole owner, can be mutated.
Initialising the parameter of a function is similar to initialising another variable (borrowing/ownership-transfer/copy...) and once inside the function the parameter is seen as any other local variable which may be mutable or not in this context.
If you intend to modify this parameter you can either declare it with mut
or transfer it to another local variable declared with mut
.
Note that we are dealing with values here, not references.
Of course, you cannot initialise a &mut T
from a &T
.
But prepending mut
on a reference (as in mut &T
or mut &mut T
) offers the ability to reassign this reference towards another value (considered mutable or not, depending on the right mut
).
If you are familiar with C or C++, this is similar to using const
before or after the star (or both sides of) when declaring a pointer.
In short, using mut
on a variable is relative to your intention to modify what is stored in this variable in your algorithm, but it is not a property of the content of this variable.