Reputation: 11
I found the following hard to understand:
fn main() {
let mut x: i32 = 10;
{
let y: &mut i32 = &mut x;
*y += 10;
println!("y={}", *y);
let z: &&mut i32 = &y;
// z += 10; // error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut i32`
// *z += 10; // binary assignment operation `+=` cannot be applied to type `&mut i32`
// **z += 10; //cannot assign to data in a `&` reference
}
println!("x={}", x);
}
When I include *z += 10
, the error message is:
error[E0368]: binary assignment operation `+=` cannot be applied to type `&mut i32`
--> src/main.rs:10:9
|
10 | *z += 10; // binary assignment operation `+=` cannot be applied to type `&mut i32`
| --^^^^^^
| |
| cannot use `+=` on type `&mut i32`
|
= help: `+=` can be used on 'i32', you can dereference `*z`: `**z`
which is exactly the same as y += 10;
Since *z
has the type &mut i32
, which is the same as y
, why can *y
be used to update the value of x
, but **z
can't?
Upvotes: 1
Views: 1550
Reputation: 28075
mut
is short for unique
There's a blurry line between "mutable" and "unique", but "mutable" may lead to the wrong intuition in this case. &mut
references are really unique references: they cannot be aliased. If you have a &mut T
, you know that while the reference exists, the T
will not be accessed (either to mutate or just to read) through any other reference.
(Although you normally need a unique reference to mutate a value, there are references that allow both aliasing and mutation. &Cell<T>
is one: you don't need unique access to a Cell
to mutate its contents. &mut
references are always unique.)
The compiler may use the knowledge that a &mut
reference cannot be aliased to perform optimizations. The Aliasing section of the Rustonomicon has some more details.
&
references are shared references&
references, on the other hand, can always be aliased by other &
references. Anything that requires unique access to a T
must guarantee that no other reference can be used to access the T
. But a &&mut T
can't guarantee that, because it could be aliased by another &&mut T
-- exclusive access to the T
is not preserved. But you can still use a &&mut T
to get a regular &T
, because that doesn't require unique access to the &mut T
.
Naturally, this is all enforced by Rust's type system. Consider how Deref
and DerefMut
are defined:
Deref::deref
takes &self
and returns &Self::Target
. So you don't need unique access to self
to get shared access to *self
.DerefMut::deref_mut
takes &mut self
to return &mut Self::Target
. So you do need unique access to self
to get unique access to *self
.And one more thing prevents you from getting a &mut T
by simply dereferencing a &&mut T
:
&mut
references don't implement Copy
.Upvotes: 5
Reputation: 8486
You missed a few mut
:
fn main() {
let mut x: i32 = 10;
{
let mut y: &mut i32 = &mut x;
*y += 10;
println!("y={}", *y);
let z: &mut &mut i32 = &mut y;
println!("z={}", z); // output: z=20
println!("*z={}", *z); // output: *z=20
println!("**z={}", **z); // output: **z=20
**z += 10;
}
println!("x={}", x);
}
y
to be mutable -> let mut y
.&y
to be mutable -> ... = &mut y
.&mut z
and reference this as mutable => let z: &mut &mut i32 = ...
I think it's more intuitive when omiting the type:
fn main() {
let mut x = 10;
{
let mut y = &mut x;
*y += 10;
println!("y={}", *y);
let z = &mut y;
**z += 10;
}
println!("x={}", x);
}
Upvotes: -1