问题
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?
回答1:
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
&selfand returns&Self::Target. So you don't need unique access toselfto get shared access to*self. - DerefMut::deref_mut takes
&mut selfto return&mut Self::Target. So you do need unique access toselfto get unique access to*self.
And one more thing prevents you from getting a &mut T by simply dereferencing a &&mut T:
&mutreferences don't implement Copy.
回答2:
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);
}
playground
- you want
yto be mutable ->let mut y. - you want
&yto be mutable ->... = &mut y. - you want to assign it to a
&mut zand 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);
}
playground
来源:https://stackoverflow.com/questions/51776161/why-cant-i-update-a-value-through-an-immutable-reference-of-a-mutable-reference