使用现有可变引用的嵌套方法调用



以下代码编译成功:

let mut v = vec![1];
let r = &mut v;
r.push(r.len());

而这个失败了:

let mut v = vec![1];
let r = &mut v;
r.push(v.len());

错误:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
|
|     let r = &mut v;
|             ------ mutable borrow occurs here
|     r.push(v.len());
|            ^ immutable borrow occurs here
|     r.push(r.len());
|     - mutable borrow later used here
  • 为什么第一个示例编译正确?是因为在外部和内部调用中使用了相同的引用:r吗?还是因为它应用了RFC 2025,两阶段借款?还是别的什么
  • 既然第一个例子成功了,为什么第二个例子失败了?为什么RFC 2025,两阶段借款不适用于此

我怀疑在第一个例子中没有错误,因为编译器不创建中间引用,而是使用相同的引用:r,这样就不会有多次借用。然而,如果是这样的话,为什么下面的代码无法编译

let mut v = vec![1];
let r = &mut v;
r.push({r.push(0);1});

在第二个示例中,当您试图获取v的长度时,它仍然是可变借用的。Vec::len&self,所以得到它的长度意味着在它已经被可变地借用的情况下,可以不可变地借用。

访问len()r仍然可以,因为您正在借用相同的借用,而不是再次借用。


请注意,即使是第一个例子在Rust 1.30及更早版本(或2015版的1.35(中也失败了,因为它依赖于NLL(非词汇生存期(。NLL的问题在于,知道什么是允许的并不是完全直观的。本质上,这意味着不超过数据词汇范围的借用是可以的,还有其他几个直观正确的情况。您的第三个例子是NLL仍然不允许的情况之一。

第一个例子:

let mut v = vec![1];
let r = &mut v;
r.push(r.len());

如果没有两阶段借用,代码将不会编译,因为外部调用创建了一个r:&mut *r的重箭头,而内部调用创建了相同值的新的不可变重箭头:&*r

对于两相借用,第一个重新借用被转换为&mut2 *r,然后当第二个重新借用超出范围时被激活。

第二个示例:

let mut v = vec![1];
let r = &mut v;
r.push(v.len());

即使是两阶段借用,它也不会编译。

内部调用导致与r冲突的v:&mut v的重新调用。

第三个示例:

let mut v = vec![1];
let r = &mut v;
r.push({r.push(0);0});

即使是两阶段借用,它也不会编译。

内部调用需要*r&mut2重借,这是2阶段借用不允许的,因为外部调用已经创建了*r&mut2重借。

参考

  • https://angelocatalani.github.io/2020-12-29-References-and-borrowing-in-Rust/
  • https://users.rust-lang.org/t/nested-method-calls-with-existing-mutable-references/53345
  • https://rust-lang.github.io/rfcs/2025-nested-method-calls.html
  • 为什么重新借用只对取消引用的指针有效

最新更新