Here are two function signatures I saw in the Rust documentation:
fn modify_foo(mut foo: Box<i32>) { *foo += 1; *foo }
fn modify_foo(foo: &mut i32) { *foo += 1; *foo }
Why the different placement of mut?
It seems that the first function could also be declared as
fn modify_foo(foo: mut Box<i32>) { /* ... */ }
If you're coming from C/C++, it might also be helpful to think of it basically like this:
// Rust C/C++
a: &T == const T* const a; // can't mutate either
mut a: &T == const T* a; // can't mutate what is pointed to
a: &mut T == T* const a; // can't mutate pointer
mut a: &mut T == T* a; // can mutate both
You'll notice that these are inverses of each other. C/C++ take a "blacklist" approach, where if you want something to be immutable you have to say so explicitly, while Rust takes a "whitelist" approach, where if you want something to be mutable you have to say so explicitly.
mut foo: T means you have a variable called foo that is a T. You are allowed to change what the variable refers to:
let mut val1 = 2;
val1 = 3; // OK
let val2 = 2;
val2 = 3; // error: re-assignment of immutable variable
This also lets you modify fields of a struct that you own:
struct Monster { health: u8 }
let mut orc = Monster { health: 93 };
orc.health -= 54;
let goblin = Monster { health: 28 };
goblin.health += 10; // error: cannot assign to immutable field
foo: &mut T means you have a variable that refers to (&) a value and you are allowed to change (mut) the referred value (including fields, if it is a struct):
let val1 = &mut 2;
*val1 = 3; // OK
let val2 = &2;
*val2 = 3; // error: cannot assign to immutable borrowed content
Note that &mut only makes sense with a reference - foo: mut T is not valid syntax. You can also combine the two qualifiers (let mut a: &mut T), when it makes sense.
The following natural language translation seems to clear things up for me...
let x = value;
x {binds immutably} to {immutable value}
let mut x = value;
x {binds mutably} to {possibly mutable value}
let x = &value;
x {binds immutably} to {a reference to} {immutable value}
let x = &mut value;
x {binds immutably} to {a reference to} {mutable value}
let mut x = &value;
x {binds mutably} to {a reference to} {immutable value}
let mut x = &mut value;
x {binds mutably} to {a reference to} {mutable value}
where
{binds mutably} means the binding can be reassigned
{mutable value} means the value's contents can change
To be able to mutate a value you need both a mutable binding and a mutable value
Note:
Reference mutability vs target mutability
A reference variable such as x, as in let x = &mut y, is a separate variable from the target variable y it is pointing to. In particular, x has its own location on the stack and mutability permissions. As such, if x is immutable, as it is here, then it cannot be reassigned to point to some other variable. That restriction is separate from the ability to mutate the target through it, as in *x = some_value; the target is a distinct variable with its own mutability permissions. However, if w is mutable, as in let mut w = &mut p, then it can indeed be reassigned to point to some other similarly typed variable: w = &mut z.
Mutable Variable of Reference Type
When you have
let mut x: &T = value;
This means that x is variable that refers to an instance of T, as if by storing the memory address of the T instance in x's value. This reference (i.e. the "memory address") is the subject of mutability in this context: x can be modified to refer to a different instance of T like this:
x = some_other_T_instance;
but the referant (i.e. the value of the T instance to which x refers) cannot be changed via x:
// Illegal
*x = a_different_value;
Immutable variable of Mutable Reference type
When you have
let x: &mut T = value;
This means that x is a variable that refers to a mutable instance of T. In this case, the referent (i.e. the actual value) is the subject of mutability. It can be modified through the reference like this
*x = some_other_value;
but the reference itself (i.e. the "memory address" in the variable x) cannot:
// illegal
x = a_different_value;
Related
This code doesn't compile. But it fails on the last line marked Err, not the line marked Ok. Why can we assign a mutable reference to an immutable reference type but not use it after the assignment?
fn main() {
let mut x = 10;
let mut y = 20;
let mut r = &x;
r = &mut y; //Ok
*r = 30; //Err
}
Why can we ... not use it after the assignment?
The variable r is an immutable reference of type &i32; it does not have mutable access to the referenced value. So it makes sense the compiler would reject your attempt to assign through it.
Why can we assign a mutable reference to an immutable reference type ...?
Why wouldn't you be able to downgrade a mutable reference into an immutable one? The latter is a strict subset of the former. If you were asking about the technicalities instead of the practicalities, its because &mut T to &T is a supported coercion.
If we add explicit types to your code as inferred by the compiler:
fn main() {
let mut x: i32 = 10;
let mut y: i32 = 20;
let mut r: &i32 = &x;
r = &mut y; //Ok
*r = 30; //Err
}
We see that r has type &i32 and not &mut i32, so of course we can't use r to modify the referenced value.
Why can we still do r = &mut y? Simply because we can always use a &mut reference anywhere a & reference is expected (because &mut T implements Deref<Target=T>, allowing coercion to happen).
Presume we have the following code, where I defined a closure named closure. Within this closure, I want to use the outer x by immutable reference (&T), and at the same time, using y by taking ownership. How can I do that? If I use move, all the outer variables that used in the closure will be moved into the closure. And also here the outer variable y is copyable.
let x: i32 = 1;
let y: i32 = 2;
let closure = || {
println!("{}", x); // make sure x is used by &T.
// println!("{}", y); // how can I use y by taking its ownership?
};
closure();
Note that capturing by moving a reference is equal to capturing by reference.
When you add a move keyword to the closure, yes, everything is captured by moving. But you can move a reference instead, which is what a closure without a move keyword does.
let x: i32 = 1;
let y: i32 = 2;
let closure = {
let x = &x;
move || {
println!("{}", x); // borrowing (outer) x
println!("{}", y); // taking the ownership of y
}
};
closure();
Consider:
fn main() {
let mut x: i32 = 5;
let y = &mut x;
let z: &i32 = y;
println!("{}", *z);
//*y = 5;
println!("{}", *y);
println!("{}", *z);
}
Without commenting out line 6 the code compiles.
It seems to me that both the reference y and z are in scope from line 5 to 7.
It sure only reads from y, but I've always been told that mutable and immutable reference can not exist at the same time. In fact, it results in a compile error if I replace line 4 with let z: &i32 = &x.
What is going on here?
Immutable references do not expect the underlying value to change and this is problematic if we do have a mutable reference. You can however have multiple immutable references because underlying data is not gonna change.
Scope of reference starts when it is first introduced and ends when it is used for the last time. That means we can add a third mutable reference underneath the println statement
fn main() {
let mut x: i32 = 5;
// scope of y starts here
// we can have multiple immutable references so I moved "mut"
let y = &x;
// scope of z starts here
let z: &i32 = y;
// scope of z ends here
println!("{}", *z);
//*y = 5;
// scope of y ends here
println!("{}", *y);
// this should work
let a = &mut x;
}
I'm wondering if someone can help me understand why this program behaves as it does:
fn main() {
let mut x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&mut y);
println!("GOT {}",*y);
}
}
This program compiles under rust 1.35.0 (both 2015 and 2018 editions), and prints
GOT 456
But, I'm confused what's going on here. I'm guessing that this is an example of an auto-dereference. So, in reality, it looks like this:
fn main() {
let mut x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&mut *y);
println!("GOT {}",*y);
}
}
Is that it?
This is a case of deref coercion, but one that is obfuscated by a few other unnecessary parts of the code. The following improvements should be made here:
The mut modifier on variable x is not needed because it is never modified.
The borrow of y in Box::new(&mut y) does not have to be mutable because the variable holds an immutable reference.
The println! implementation also knows to print values behind references, so the explicit * is not needed.
Then, we get the following code:
fn main() {
let x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&y);
println!("GOT {}", y);
}
}
y is a variable of type Box<&i32> which is initially bound to a box created in the outer scope. The subsequent assignment to a new box works because the &y, of type &Box<&i32>, is coerced to &&i32, which can then be put in the box by automatically dereferencing the first borrow. This coercion is required because the variable x can only be assigned values of the same Box<&i32> type.
The lifetime of the reference inside both boxes also ended up being the same, because they refer to the same value in x.
See also:
What is the relation between auto-dereferencing and deref coercion?
How could know the type of a binding if I use auto type deduction when creating a binding? what if the expression on the right side is a borrow(like let x = &5;), will it be value or a borrow? What will happen if I re-assign a borrow or a value?
Just for check, I do can re-assign a borrow if I use let mut x: &mut T = &mut T{}; or let mut x:&T = & T{};, right?
I sense some confusion between binding and assigning:
Binding introduces a new variable, and associates it to a value,
Assigning overwrites a value with another.
This can be illustrated in two simple lines:
let mut x = 5; // Binding
x = 10; // Assigning
A binding may appear in multiple places in Rust:
let statements,
if let/while let conditions,
cases in a match expression,
and even in a for expression, on the left side of in.
Whenever there is a binding, Rust's grammar also allows pattern matching:
in the case of let statements and for expressions, the patterns must be irrefutable,
in the case of if let, while let and match cases, the patterns may fail to match.
Pattern matching means that the type of the variable introduced by the binding differs based on how the binding is made:
let x = &5; // x: &i32
let &y = &5; // y: i32
Assigning always requires using =, the assignment operator.
When assigning, the former value is overwritten, and drop is called on it if it implements Drop.
let mut x = 5;
x = 6;
// Now x == 6, drop was not called because it's a i32.
let mut s = String::from("Hello, World!");
s = String::from("Hello, 神秘德里克!");
// Now s == "Hello, 神秘德里克!", drop was called because it's a String.
The value that is overwritten may be as simple as an integer or float, a more involved struct or enum, or a reference.
let mut r = &5;
r = &6;
// Now r points to 6, drop was not called as it's a reference.
Overwriting a reference does not overwrite the value pointed to by the reference, but the reference itself. The original value still lives on, and will be dropped when it's ready.
To overwrite the pointed to value, one needs to use *, the dereference operator:
let mut x = 5;
let r = &mut x;
*r = 6;
// r still points to x, and now x = 6.
If the type of the dereferenced value requires it, drop will be called:
let mut s = String::from("Hello, World!");
let r = &mut s;
*r = String::from("Hello, 神秘德里克!");
// r still points to s, and now s = "Hello, 神秘德里克!".
I invite you to use to playground to and toy around, you can start from here:
fn main() {
let mut s = String::from("Hello, World!");
{
let r = &mut s;
*r = String::from("Hello, 神秘德里克!");
}
println!("{}", s);
}
Hopefully, things should be a little clearer now, so let's check your samples.
let x = &5;
x is a reference to i32 (&i32). What happens is that the compiler will introduce a temporary in which 5 is stored, and then borrow this temporary.
let mut x: &mut T = T{};
Is impossible. The type of T{} is T not &mut T, so this fails to compile. You could change it to let mut x: &mut T = &mut T{};.
And your last example is similar.