引用与生命周期的底层实现

原理

Rust 引用在运行时与 C 指针完全等效——都是 8B 虚拟地址。区别在编译期:borrow checker 为 &T 附加 noalias 属性传给 LLVM,为 &mut T 附加 unique + noalias 属性。

LLVM noalias 属性告知优化器:通过该指针访问的内存没有其他指针同时访问。这使得:

  • Load/store 可以重排
  • 值可以缓存到寄存器不重新加载
  • 向量化时可以忽略别名冲突

借用规则的检查在 MIR 层执行(NLL borrowck),通过求解变童(variable)在每条语句上的存活性约束来工作。每条 & 生成 borrow,每条 &mut 生成 unique borrow,borrows 与所有权之间的关系用 disjoint intervals 表示。

宽指针:&str&[T] 是 fat pointer(16B),包含数据指针和长度。&dyn Trait 包含数据指针和 vtable 指针。这些在调用时通过寄存器对传递。


语法

引用与指针

let x = 5;
let r: &i32 = &x;     // 编译后等价于 *const i32
let v = *r;            // 解引用
 
// 裸指针(unsafe)
let p: *const i32 = &x as *const i32;
unsafe { println!("{}", *p); }

借用检查的核心规则

规则示例
共享借用可复制let r1 = &s; let r2 = &s;
独占借用唯一let r = &mut s;
二者不能共存&s&mut s 不可同时有效
被借时不可 move&s 时不能 let t = s

noalias 优化效果

fn add(a: &mut i32, b: &i32) { *a += *b; }
 
// 编译器可为 a 生成 noalias load/store
// 因为 &mut 保证无其他引用指向同一位置

fat pointer 布局

类型栈大小组成部分
&T8B数据指针
&[T]16Bptr + len
&str16Bptr + len
&dyn Trait16Bdata ptr + vtable ptr
&mut T8B数据指针

实践

力扣问题

力扣: 力扣DFS — 引用传递

AI 自检

  1. noalias 属性具体影响 LLVM 哪些优化 pass?举一个特定优化的例子。
  2. &str 的 fat pointer 为何是 16B 而非 8B?长度信息存在的必要性是什么?