Rust 为什么可以';我是否通过可变值的可变引用的不可变引用来更新值?

Rust 为什么可以';我是否通过可变值的可变引用的不可变引用来更新值?,rust,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 opera

我发现以下几点很难理解:

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]:二进制赋值操作`+=`不能应用于类型`&mut i32`
-->src/main.rs:10:9
|
10 |*z+=10;//二进制赋值操作“+=”不能应用于类型“%mut i32”`
|         --^^^^^^
|         |
|无法在类型“%mut i32”上使用“+=”`
|
=帮助:`+=`可以在'i32'上使用,您可以取消对`*z`:`**z的引用`
y+=10完全相同


既然
*z
的类型为
&mut i32
,与
y
相同,为什么
*y
可以用来更新
x
的值,而
**z
不能呢?

您遗漏了一些
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
    是可变的->
    let mut y
  • 您希望
    &y
    是可变的->
    ..=&mut y
  • 您希望将其分配给一个
    &mut z
    并将其引用为mutable=>
    让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);
}
mut
unique
“可变”和“独特”之间的界限模糊,但在这种情况下,“可变”可能会导致错误的直觉<代码>&mut
引用实际上是唯一的引用:它们不能被别名。如果您有一个
&mut T T
,您就知道当引用存在时,
T
将不会通过任何其他引用进行访问(变异或只是读取)

(虽然您通常需要唯一的引用来变异值,但也有一些引用同时允许别名和变异。
&Cell
就是其中之一:您不需要唯一访问
单元格来变异其内容。
&mut
引用始终是唯一的。)

编译器可以使用
&mut
引用不能被别名的知识来执行优化。报告还有一些细节

&
引用是共享引用 另一方面,
&
引用始终可以被其他
&
引用使用别名。任何需要唯一访问
T
的内容都必须保证不能使用其他引用访问
T
。但是
&&mut T T
不能保证这一点,因为它可能会被另一个
&&mut T T
使用别名——对
T
的独占访问不会被保留。但是您仍然可以使用
&mut T
获取常规
&T
,因为这不需要对
&mut
的唯一访问

当然,这一切都是由Rust的类型系统强制执行的。考虑< <代码> Deref < /代码>和<代码> DerefMut < /> >定义:

  • 获取
    &self
    并返回
    &self::Target
    。因此,您不需要唯一访问
    self
    ,就可以共享访问
    *self
  • 使用
    &mut self
    返回
    &mut self::Target
    。因此,您确实需要唯一访问
    self
    才能唯一访问
    *self
  • 还有一件事阻止您通过简单地取消对
    &&mut T T
    的引用而获得
    &mut T T

  • &mut
    引用无法实现

  • 我理解OP是在问为什么,而不是我需要做什么更改才能让代码正常工作。但我不希望
    y
    本身是可变的。这里我希望
    y
    借用的值是可变的。在此示例中,
    z
    将能够更改
    y
    的值,如
    *z=&mut i,这不是我想要的。这在这里是不可能的。当查看我的第二个代码片段时,您可以看到原因:如果引用本身不可变,则不能将引用作为可变引用借用。在这种情况下,我们希望将
    z
    放在一个块中。具体来说,我的意思是像这个。我仍然不明白为什么
    mut
    是必要的,即使
    *z
    的类型与
    y
    相同。我认为错误的原因解释得很好,我不这么认为。如果以
    *z+=10运行它
    并且它将提供
    帮助:+=可用于“i32”,您可以取消引用*z:*z
    。误差与
    y+=10完全相同,但
    *y+=10将起作用,
    **z+=10不起作用。
    &mut
    引用确实是唯一的引用:它们不能有别名-挑剔:。虽然我们通常对这个术语很放松,但在这些细节的问题上,非常具体是值得的。答案中还有其他一些类似的问题。@Shepmaster我使用这个术语的方式与Nomicon相同:关于活跃度和重传有一些争论,但我不同意您的示例演示了
    &mut
    引用的别名。谢谢您的回答。读了之后,不知何故我觉得
    &
    只是语言的一个概念,如果我们有
    让y:&mut 32=&mut xy
    ,对吗?@fishbone我不知道你的确切意思。如果您熟悉C,Rust的底层执行模型与C的基本相同,但是
    &
    &mut
    引用是语言特性,允许编译器更容易地对难以或不可能用C表示的关系进行推理。这是否开始解决您的问题?@trentcl感谢您的解释。很清楚。