Skip to content

生命周期标注语法

> 掌握生命周期标注的语法和方法,理解不同标注方式对函数行为的影响。

生命周期标注语法详解

从问题到解决方案

rust
// 问题场景:返回两个字符串中较长的
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}

// ❌ 编译错误:
// error[E0106]: missing lifetime specifier
//  --> src/main.rs:1:21
//   |
// 1 | fn longest(x: &str, y: &str) -> &str {
//   |                     -        -  ^ expected named lifetime parameter
//   |                     |        |
//   |                     |        let's call the lifetime of this reference `'1`
//   |                     let's call the lifetime of this reference `'2`
//   |
//   = help: consider introducing a named lifetime parameter
// help: consider introducing a named lifetime parameter
//   |
// 1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
//   |           ++++     ++          ++          ++
▶ Run

为什么需要生命周期标注?

编译器的困境:

x: &str  ──┐
           ├─> 返回值可能来自 x 或 y
y: &str  ──┘

问题:
• x 和 y 有不同的生命周期
• 编译器不知道返回的引用来自哪个参数
• 无法保证返回引用的安全性

┌───────────────────────────────────────────────────┐
│         为什么不能自动推断?                        │
├───────────────────────────────────────────────────┤
│                                                   │
│  x 的生命周期:━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━    │
│                                                   │
│  y 的生命周期:━━━━━━━━━━━━━━━━━                  │
│                                                   │
│  返回值可能来自 x 或 y,编译器无法确定:           │
│  • 如果返回 x:生命周期 = x 的生命周期             │
│  • 如果返回 y:生命周期 = y 的生命周期             │
│  • 运行时才能决定,编译时无法验证                  │
│                                                   │
└───────────────────────────────────────────────────┘

基本语法

rust
// 生命周期参数以撇号开头,通常小写
// 常见命名:'a, 'b, 'c, ...

// 带生命周期的引用
let r: &'a i32;           // 不可变引用
let r: &'a mut i32;       // 可变引用

// 多个生命周期
let r: &'a &'b i32;       // 引用的引用
let r: &'a mut &'b i32;   // 可变引用

// 生命周期参数的位置
fn function<'a>(param: &'a i32) { }
struct Struct<'a> { field: &'a str }
enum Enum<'a> { Variant(&'a str) }
▶ Run

生命周期标注的位置

rust
// 1. 函数参数和返回值
fn foo<'a>(x: &'a str) -> &'a str {
    x
}

// 2. 结构体字段
struct Bar<'a> {
    name: &'a str,
}

// 3. 枚举变体
enum Baz<'a> {
    Name(&'a str),
    Value { data: &'a str },
}

// 4. impl 块
impl<'a> Bar<'a> {
    fn get(&self) -> &str {
        self.name
    }
}
▶ Run

函数中的生命周期深度解析

longest 函数详细分析

rust
// ✅ 正确:显式标注生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// 'a 的含义:
// 1. x 和 y 都至少在生命周期 'a 内有效
// 2. 返回的引用也只在 'a 内有效
// 3. 调用者保证 x 和 y 都足够"长寿"
▶ Run

内存布局图解:

场景 1:两个字符串生命周期相同

let s1 = String::from("hello");   // 's1 开始
let s2 = String::from("world");   // 's2 开始
let result = longest(&s1, &s2);   // 'result 开始
println!("{}", result);           // 使用 result

时间线:
┌────────────────────────────────────────────────┐
│ s1: "hello"                                    │
│ s2: "world"     (同时开始,同时结束)            │
│ result ──────────────────────────────────┐    │
└────────────────────────────────────────────┼───┘

生命周期 'a = s1 和 s2 的交集 ───────────────┘

栈内存:
┌──────────────────┐
│ s1 ────────┐      │
│ ptr        │ hello│
│ len        │ 5    │
│ cap        │ 5    │
└────────────┴──────┘
│ s2 ────────┐      │
│ ptr        │ world│
│ len        │ 5    │
│ cap        │ 5    │
└────────────┴──────┘
│ result ────┐      │
│            ↓      │
│ 指向 s1 或 s2     │
└──────────────────┘

场景 2:两个字符串生命周期不同

let s1 = String::from("long string");  // 's1 开始
{
    let s2 = String::from("short");      // 's2 开始
    let result = longest(&s1, &s2);      // 'result 开始
    println!("{}", result);              // ✅ result 在 s2 前失效
}                                         // s2 和 result 失效
println!("{}", s1);                      // s1 仍有效

时间线:
┌────────────────────────────────────────────────┐
│ s1: "long string"                              │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  │
│                                                │
│      s2: "short"                               │
│      ━━━━━━━━━━━━━━━━━                         │
│           ↑                                    │
│      result 的生命周期                          │
│      (受限于 s2,较短者)                        │
└────────────────────────────────────────────────┘

生命周期约束:
'a = min('s1, 's2) = 's2
result 的生命周期不能超过 s2

实际使用案例

rust
fn main() {
    let s1 = String::from("long string");  // 's1
    let s2 = String::from("short");         // 's2

    // 'a 是 's1 和 's2 的交集
    let result = longest(&s1, &s2);

    // result 的生命周期受限于 s1 和 s2 中较短的那个
    println!("结果:{}", result);
}  // result 先失效,然后 s2,最后 s1
▶ Run

不同生命周期标注的影响

rust
// 情况 1:返回与第一个参数相同生命周期
fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    x  // 只返回 x,与 y 无关
}

fn main() {
    let s1 = String::from("hello");
    let result;
    {
        let s2 = String::from("world");
        result = first(&s1, &s2);
        // ✅ result 的生命周期 = s1 的生命周期
        // 即使 s2 失效,result 仍然有效(因为来自 s1)
    }
    println!("{}", result);  // ✅ 仍然有效
}

// 情况 2:返回与第二个参数相同生命周期
fn second<'a, 'b>(x: &'a str, y: &'b str) -> &'b str {
    y  // 只返回 y,与 x 无关
}

fn main() {
    let result;
    let s1 = String::from("hello");
    {
        let s2 = String::from("world");
        result = second(&s1, &s2);
        // ❌ result 的生命周期 = s2 的生命周期
    }
    // println!("{}", result);  // 错误:s2 已失效
}

// 情况 3:返回受限于两者(最常见)
fn either<'a>(x: &'a str, y: &'a str) -> &'a str {
    // 可能返回 x 或 y,所以返回受限于两者
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
▶ Run

生命周期标注对比图:

函数签名对比:

fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str
         ││         │            │            │
         ││         │            │            └─ 返回 x 的生命周期
         ││         │            └────────────── y 有独立生命周期
         ││         └─────────────────────────── x 的生命周期
         │└───────────────────────────────────── y 的生命周期参数
         └───────────────────────────────────── x 的生命周期参数

内存关系:
x:  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                    ↑ 返回值
y:      ━━━━━━━━━━━━━━━━━
        (y 的生命周期不影响返回值)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

fn either<'a>(x: &'a str, y: &'a str) -> &'a str
         │         │            │            │
         │         │            │            └─ 返回值受限于两者
         │         │            └────────────── y 必须至少活 'a
         │         └─────────────────────────── x 必须至少活 'a
         └───────────────────────────────────── 同一个生命周期参数

内存关系:
x:  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

y:      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        ↑           ↑
        └─ 'a 开始  └─ 'a 结束(较短者决定)

生命周期标注的含义

rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    // 'a 的含义:
    // 1. x 和 y 都至少在生命周期 'a 内有效
    // 2. 返回的引用也只在 'a 内有效
    // 3. 调用者保证 x 和 y 都足够"长寿"

    if x.len() > y.len() {
        x  // 返回 x,要求 x 在 'a 内有效
    } else {
        y  // 返回 y,要求 y 在 'a 内有效
    }
}

fn main() {
    let s1 = String::from("long string");  // 's1
    let s2 = String::from("short");         // 's2

    // 'a 是 's1 和 's2 的交集
    let result = longest(&s1, &s2);

    // result 的生命周期受限于 s1 和 s2 中较短的那个
    println!("结果:{}", result);
}  // result 先失效,然后 s2,最后 s1
▶ Run

不同生命周期标注的影响

rust
// 情况 1:返回与第一个参数相同生命周期
fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    x  // 只返回 x,与 y 无关
}

// 情况 2:返回与第二个参数相同生命周期
fn second<'a, 'b>(x: &'a str, y: &'b str) -> &'b str {
    y  // 只返回 y,与 x 无关
}

// 情况 3:返回受限于两者(最常见)
fn either<'a>(x: &'a str, y: &'a str) -> &'a str {
    // 可能返回 x 或 y,所以返回受限于两者
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let s1 = String::from("hello");
    let s2 = String::from("world");

    let r1 = first(&s1, &s2);   // r1 的生命周期 = s1 的生命周期
    let r2 = second(&s1, &s2);  // r2 的生命周期 = s2 的生命周期
    let r3 = either(&s1, &s2);  // r3 受限于 s1 和 s2
}




---

## 小结

- 生命周期标注 `'a` 告诉编译器引用之间的关联关系
- 单一生命周期 `'a` 表示所有引用至少活到 `'a` 结束
- 不同生命周期 `'a`, `'b` 可以表达更精细的约束
- 返回值生命周期受限于输入参数中较短的那个
- 生命周期标注不改变实际生命周期,只是描述约束

## 练习题

详见:[练习题](../../exercises/17-lifetimes)
▶ Run