跳到主要内容

borrow-advanced

借用检查进阶:NLL、两阶段借用、重借用

NLL(Non-Lexical Lifetimes)

  • 借用的作用域不再严格等于“词法作用域”,而是更接近“最后一次使用结束”。
  • 好处:减少很多看似无害但过去无法通过编译的借用冲突。
let mut s = String::from("hi");
let r = &s; // 不可变借用开始
println!("{}", r); // 最后一次使用 r
// 从此处起 r 的借用结束(NLL 生效)
s.push('!'); // 允许可变借用

两阶段借用(Two-phase Borrow)

  • 可变借用在“创建阶段”与“使用阶段”之间分离,允许在创建后至真正使用前,与不可变借用短暂共存。
let mut v = vec![1,2,3];
let i = v.len(); // 不可变借用读取长度
v.push(i); // 两阶段借用:push 的 &mut v 创建后直到调用内部才生效

注意:两阶段借用并非任意放宽;一旦进入“使用阶段”,仍需遵守独占规则。

重借用(Reborrowing)

  • 从一个借用再创建新借用:&mut T 可临时重借为 &T 供只读;或 &mut T 重借为新的 &mut T 限定于更小作用域。
fn inc(x: &mut i32) {
let y: &i32 = &*x; // 从 &mut 重借为 &,只读
println!("{}", y);
*x += 1; // 原可变借用继续使用
}

方法接收者与借用

  • &self/&mut self 决定借用种类;链式方法中借用结束点由 NLL 推断。
  • 典型误区:从容器获得引用后再触发可能的 reallocate(如 push),将导致先前引用失效;应重新获取或使用索引。
let mut v = vec![0];
let r = &v[0]; // 借用元素
v.push(1); // 可能 reallocate,r 失效(编译器通常拒绝此模式)
println!("{}", r);

Polonius(研究中)

  • 新的借用检查器模型,基于 Datalog,更精确地区分错误与合法模式,仍在推进中。

调试借用问题的技巧

  • 缩小可变借用作用域(花括号或临时变量)
  • 使用 drop(x) 提前结束借用
  • 拆分集合操作,避免在持有引用时触发生长/移动