为什么我不能通过可变值的可变引用的不可变引用来更新值?

时间:2018-08-09 21:43:32

标签: rust

我发现以下内容难以理解:

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);
}

当我包含*z += 10时,错误消息是:

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`

y += 10;

完全相同

由于*z的类型为&mut i32,与y相同,所以为什么可以使用*y来更新x的值,但是**z不能?

2 个答案:

答案 0 :(得分:3)

mutunique的缩写

“可变”和“唯一”之间存在模糊的界线,但是在这种情况下,“可变”可能会导致错误的直觉。 &mut引用实际上是唯一引用:它们不能别名。如果您有&mut T,则知道在引用存在的情况下T将不会通过 any <进行访问(无论是变异还是只是读取) / em>其他参考。

(尽管通常需要一个唯一的引用来变异一个值,但是有些引用同时允许别名和突变。&Cell<T>是其中之一:您不需要唯一访问Cell来变异它的内容。&mut引用始终是唯一的。)

编译器可能会使用以下知识:不能引用&mut引用来进行优化。 Aliasing section of the Rustonomicon有更多详细信息。

&引用是共享引用

另一方面,

&引用始终可以被其他&引用作为别名。任何需要对T进行唯一访问的内容都必须保证不能使用其他引用来访问T。但是&&mut T不能保证,因为它可能被另一个 &&mut T别名-不保留对T的独占访问。但是您仍然可以使用&&mut T来获得常规的&T,因为这不需要对&mut T的唯一访问。

自然,这都是由Rust的类型系统强制执行的。考虑如何定义DerefDerefMut

  1. Deref::deref接受&self并返回&Self::Target。因此,您无需拥有对self的唯一访问权限即可获得对*self的共享访问权限。
  2. DerefMut::deref_mut&mut self返回&mut Self::Target。因此,您要做需要对self的唯一访问权限才能获得对*self的唯一访问权限。

还有一件事情使您仅通过解除引用&mut T就可以阻止您获得&&mut T

  1. &mut引用未实现Copy

答案 1 :(得分:-1)

您错过了一些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

  • 您希望y可变-> let mut y
  • 您希望&y可变-> ... = &mut y
  • 您要将其分配给&mut z并将其引用为可变=> let z: &mut &mut i32 = ...

我认为省略类型会更直观:

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