本页目录

生命周期

生命周期('a)不是运行时的值——它是编译期对引用有效作用域的约束。从 elision 规则(编译器自动推导)到显式标注(struct/variance/HRTB),生命周期标注本质上是在告诉借用检查器"这几个引用之间谁活得比谁长"。

Lifetime 是编译期概念

Lifetime 参数('a)不是运行时的值——它们只是编译期的约束⁠:这个引用的有效作用域不能比 'a 更长。编译器在 MIR 上用 region inference 推导最小 lifetime。

Lifetime Elision

大多数函数不需要显式标注 lifetime——编译器根据 3 条规则自动推导:

  1. 每个不标注 lifetime 的引用参数被分配独立的 lifetime(fn foo<'a, 'b>(x: &'a T, y: &'b U)
  2. 如果只有一个 input lifetime,它被赋给所有 output lifetime(fn foo<'a>(x: &'a T) -> &'a U
  3. 如果多个 input lifetime 中有 &self&mut selfself 的 lifetime 赋给所有 output

规则 2 是最常见的:单个引用参数 → 返回的引用默认借用该参数。规则 3 是 why method chaining works:self 的 borrow 传递到返回值。

Struct 中的 Lifetime

当 struct 包含引用时,必须显式标注 lifetime:

struct Excerpt<'a> {
    part: &'a str,                   // 这个引用不能比 'a 活得更久
}

impl<'a> Excerpt<'a> {
    fn announce(&self, msg: &str) -> &str {  // rule 3: output borrows self
        println!("{}", msg);
        self.part
    }
}

编译器的保证:Excerpt<'a> 实例不能活得比被引用的 &'a str 更久。

'static

'static 是唯一有"特殊"含义的 lifetime——它表示引用的数据存活于整个程序生命周期。来源:

  • 字符串字面量:"hello" 的类型是 &'static str——编译期嵌入二进制,始终有效
  • Box::leak(Box::new(T)) 可以制造 &'static mut T
  • static 变量和 const 引用

Variance

Rust 的 lifetime 有 subtyping variance。理解它的关键规则:

  • 'long: 'short(即 'long 活得比 'short 长,所以 'long 满足 'short 的约束)
  • &'a T: covariant in 'a(如果 'long: 'short,则 &'long T 可用于 &'short T
  • &'a mut T: invariant in 'a(不能缩短可变引用的 lifetime——否则可能违反 aliasing XOR mutation)
fn assign<T>(to: &mut T, from: T) { *to = from; }

let mut x: &'static str = "hello";
{
    let y = String::from("world");
    // assign(&mut x, y.as_str());   // ERROR: T = &'static str (from to), but y is &'y str
}
// 如果这能编译, x 指向 y 的数据 → y 被 drop → 悬垂指针

HRTB (Higher-Ranked Trait Bounds)

当 closure 参数有 lifetime 但不需要显式声明时:

fn apply<F>(f: F) where F: for<'a> Fn(&'a str) -> &'a str { ... }
// "f 对于**任意** lifetime 'a 都满足 Fn(&'a str) -> &'a str"
// 不需要 caller 处指定 'a — 编译器自动量化

PhantomData

当 struct 有 lifetime 但没有直接存储引用——用 PhantomData 标记所有权/借用关系:

use std::marker::PhantomData;

struct MyIter<'a, T> {
    ptr: *const T,                   // 裸指针 — 不携带 Rust 的借用关系!
    _marker: PhantomData<&'a T>,     // 告诉编译器: "这个 struct 逻辑上持有 &'a T"
}

参考

  • Rust Book: Chapter 10.3 — Validating References with Lifetimes
  • Rust Reference: lifetime elision, variance
  • RFC: RFC 2094 (NLL), RFC 387 (Higher-Ranked Trait Bounds)

Keywords: lifetime, elision, 'static, variance, covariant, invariant, HRTB, PhantomData, region inference