【发布时间】: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。 -
@domin 不,
<&&mut i32 as Deref>::Target的类型是&mut i32,正如您从上面链接的文档中看到的那样。查看Deref特征的定义在这里并没有真正的帮助,因为相关步骤是内置在编译器中的,而不是标准库中。 -
有时考虑exclusive和shared引用而不是mutable和immutable会更有帮助> 的。所以
y是对整数的独占引用,z是对整数独占引用的共享引用,但是不能取消引用z得到y-like exclusive i> 访问整数,因为这违反了z自己的共享性合同。 -
@domin 正如我之前所说,借用检查器是一个复杂的野兽,但根本不需要了解它是如何工作的。您可以相信它会维护它应该维护的不变量。并对不同的错误进行良好的观察——在此处显式添加类型确实会改变语义。如果没有类型,let 绑定会移动可变引用,而使用显式类型注释,绑定会创建隐式重借。这只发生在可变引用上,并且是一个相当微妙的区别。
标签: rust immutability borrow-checker mutability