以下代码编译成功:
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
- 为什么重新借用只对取消引用的指针有效