【问题标题】:How to interpret immutable references to mutable types in Rust?如何解释 Rust 中对可变类型的不可变引用?
【发布时间】:2019-05-06 12:21:22
【问题描述】:

如果我的解引用链中有 any 不可变引用,我似乎无法改变任何内容。一个样本:

fn main() {
    let mut x = 42;
    let y: &mut i32 = &mut x; // first layer
    let z: &&mut i32 = &y; // second layer
    **z = 100; // Attempt to change `x`, gives compiler error.

    println!("Value is: {}", z);
}

我收到编译器错误:

error[E0594]: cannot assign to `**z` which is behind a `&` reference
 --> src/main.rs:5:5
  |
4 |     let z: &&mut i32 = &y; // second layer
  |                        -- help: consider changing this to be a mutable reference: `&mut y`
5 |     **z = 100; // Attempt to change `x`, gives compiler error.
  |     ^^^^^^^^^ `z` is a `&` reference, so the data it refers to cannot be written

在某种程度上,这是有道理的,否则编译器将无法阻止对同一个变量具有多个可变访问路径。

但是,在查看类型时,语义似乎是违反直觉的:

  • 变量y 的类型为&mut i32,或者用简单的英文“A mutable reference to an integer”。
  • 变量z 的类型为&&mut i32,或者用简单的英文“An immutable reference to a mutable reference to an integer”。
  • 通过取消引用z 一次(即*z),我将得到&mut i32 类型的东西,即与y 类型相同的东西。然而,再次解除引用(即**z)得到i32类型的东西,但我不能改变那个整数。

本质上,某种意义上的引用类型对我来说是骗人的,因为它们实际上并没有像他们声称的那样做。在这种情况下,我应该如何正确阅读参考文献的类型,或者我该如何恢复对该概念的信心?

使用此示例进行测试:

fn main() {
    let mut x = 42;
    let y: &mut i32 = &mut x; // first layer
    let m: &&mut i32 = &y; // second layer
    let z: &&&mut i32 = &m; // third layer
    compiler_builtin_deref_first_layer(*z);
}

fn compiler_builtin_deref_first_layer(v: &&mut i32) {
    compiler_builtin_deref_second_layer(*v);
}

fn compiler_builtin_deref_second_layer(w: &mut i32) {
    println!("Value is: {}", w);
}

最后两个函数的参数类型是正确的。如果我更改其中任何一个,编译器会抱怨类型不匹配。但是,如果我按原样编译示例,则会收到此错误:

error[E0596]: cannot borrow `**v` as mutable, as it is behind a `&` reference

不知何故,拨打compiler_builtin_deref_first_layer 似乎没问题,但拨打compiler_builtin_deref_second_layer 却不行。编译器错误谈论**v,但我只看到*v

【问题讨论】:

  • “通过取消引用z 一次(即*z)我会得到&mut i32 类型的东西” 不,那将是一个可变的deref,它不能通过不可变引用完成。您最多可以从那里获得&i32
  • 那些是DerefDerefMut。每个特征的文档解释了在哪个上下文中使用哪个。
  • @domin 不,<&&mut i32 as Deref>::Target 的类型是 &mut i32,正如您从上面链接的文档中看到的那样。查看 Deref 特征的定义在这里并没有真正的帮助,因为相关步骤是内置在编译器中的,而不是标准库中。
  • 有时考虑exclusiveshared引用而不是mutableimmutable会更有帮助> 的。所以y是对整数的独占引用,z是对整数独占引用的共享引用,但是不能取消引用z得到y-like exclusive i> 访问整数,因为这违反了z 自己的共享性合同。
  • @domin 正如我之前所说,借用检查器是一个复杂的野兽,但根本不需要了解它是如何工作的。您可以相信它会维护它应该维护的不变量。并对不同的错误进行良好的观察——在此处显式添加类型确实会改变语义。如果没有类型,let 绑定会移动可变引用,而使用显式类型注释,绑定会创建隐式重借。这只发生在可变引用上,并且是一个相当微妙的区别。

标签: rust immutability borrow-checker mutability


【解决方案1】:

本质上,某种意义上的引用类型对我来说是骗人的,因为它们实际上并没有像他们声称的那样做。在这种情况下,我应该如何正确阅读参考文献的类型,或者我该如何恢复对该概念的信心?

在 Rust 中读取引用的正确方法是作为权限。

一个对象的所有权,当它没有被借用时,允许你对这个对象做任何你想做的事情;创建它,摧毁它,将它从一个地方移动到另一个地方。您是所有者,您可以为所欲为,您可以控制该对象的生命。

可变引用从所有者那里借用对象。当可变引用处于活动状态时,它授予对对象的独占访问权限。没有其他人可以读取、写入或对对象执行任何其他操作。可变引用也可以是调用和独占引用,或独占借用。您必须将对象的控制权交还给原始所有者,但与此同时,您可以随心所欲地使用它。

不可变引用或共享借用意味着您可以与其他人同时访问它。因此,您只能阅读它,而没有人可以修改它,否则将根据操作发生的确切顺序产生不确定的结果。

可变(或独占)引用和不可变(或共享)引用都可以对拥有的对象进行,但这并不意味着当您通过引用引用该对象时您就拥有该对象。您可以对对象执行的操作取决于您通过何种参考来访问它。

所以不要将&&mut T 引用视为“对 T 的可变引用的不可变引用”,然后想“好吧,我不能改变外部引用,但我应该能够改变内部参考。”

相反,可以将其视为“某人拥有T。他们已授予独家访问权限,因此现在有人有权修改T。但与此同时,该人已授予退出对&mut T的共享访问,这意味着他们已承诺在一段时间内不会对其进行变异,并且所有用户都可以使用对&mut T的共享引用,包括取消对底层T的引用,但仅限对于您通常可以通过共享引用完成的事情,这意味着阅读而不是写作。”

最后要记住的是,可变或不可变部分实际上并不是引用之间的根本区别。这真的是独家与共享的部分。在 Rust 中,您可以通过共享引用来修改某些内容,只要有某种内部保护机制可以确保一次只有一个人这样做。有多种方法可以做到这一点,例如CellRefCellMutex

所以&T&mut T 提供的并不是真正的不可变或可变访问,尽管它们被如此命名是因为这是它们在没有任何库功能的情况下在语言级别提供的默认访问级别。但它们真正提供的是共享或独占访问,然后数据类型上的方法可以为调用者提供不同的功能,具体取决于它们采用的是拥有值、独占引用还是共享引用。

所以将引用视为权限;并且它是您通过它达到某些东西的参考,它决定了您可以使用它做什么。当您拥有所有权或独占引用时,提供独占或共享引用会暂时阻止您在那些借用的引用仍然存在时可变地访问对象。

【讨论】:

  • 非常感谢您的详细解答!因此,多个堆叠引用仍应被解读为直接或间接指向同一个底层拥有值 (T) 的引用?如果是这种情况,那我为什么要使用let foo = &mut &mut x;(其中x : T),然后在中途取消引用该引用并改变中间引用,如下所示:*foo = &mut y; 其中y 是另一个T 类型的值?此操作与原始 T 无关,因此引用必须是一个对象(具有所有者)本身......
  • 是的,引用是一个拥有自己的对象。但是您获得的许可取决于您到达它的路径。如果您通过 & 引用到达 &mut 引用,则您对引用本身及其引用的任何内容只有共享的只读访问权限。
  • 我可能应该澄清一下:多个堆叠引用不只是作为指向基础值的引用来读取。他们指向他们所指向的东西。但是您获得的权限是您到达它的路径中每个引用所授予的最小权限。我将编辑我的答案以更清楚。
  • @domin 是的,没错。借用检查器是静态工作的,虽然可以静态分析这段特定的代码,但如果您将它们传递给函数,它将无法判断函数返回后引用了哪个引用 foo ( play.rust-lang.org/…)。所以编译器应用保守的规则;它确定foo 可以引用xy,因此只要foo 存在,就不能对xy 进行其他引用。
  • 对。那么典型的集合并集分析!非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 2011-08-23
  • 1970-01-01
相关资源
最近更新 更多