bigywater.tech
Rust · 23

生命周期标注到底描述什么

生命周期标注 'a 是 Rust 里最容易被误解的语法:它不是「让某个值活更久」,而是「描述几个引用的有效期之间的关系」。这篇用一个 longest 函数,从四个角度验证这一点,最后用汇编确认它在运行时完全不存在。

E0106
返回引用但没说借自哪个输入
E0597
返回值在较短的输入之后使用
0
生命周期在运行时占用的指令数(标注前后机器码相同)

为什么需要标注

实测 · 本机执行输出
  源码:
      1 | fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }
      2 | fn main() { println!("{}", longest("abc", "de")); }
  编译:失败
    error[E0106]: missing lifetime specifier
     --> <实验目录>/src/u23a.rs:1:33
      |
    1 | fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }
      |               ----     ----     ^ expected named lifetime parameter
      |
      = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
    help: consider introducing a named lifetime parameter
      |
    1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
      |           ++++     ++          ++          ++
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0106`.
  源码:
      1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
      2 | fn main() { println!("{}", longest("abc", "de")); }
  编译:成功
  运行(退出码 0):
    abc

标注描述的是关系

实测 · 本机执行输出
  源码:
      1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
      2 | fn main() {
      3 |     let a = String::from("long string");
      4 |     let result;
      5 |     {
      6 |         let b = String::from("xyz");
      7 |         result = longest(a.as_str(), b.as_str());
      8 |     }
      9 |     println!("{}", result);
     10 | }
  编译:失败
    error[E0597]: `b` does not live long enough
     --> <实验目录>/src/u23c.rs:7:38
      |
    6 |         let b = String::from("xyz");
      |             - binding `b` declared here
    7 |         result = longest(a.as_str(), b.as_str());
      |                                      ^ borrowed value does not live long enough
    8 |     }
      |     - `b` dropped here while still borrowed
    9 |     println!("{}", result);
      |                    ------ borrow later used here
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0597`.
  源码:
      1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
      2 | fn main() {
      3 |     let a = String::from("long string");
      4 |     {
      5 |         let b = String::from("xyz");
      6 |         let result = longest(a.as_str(), b.as_str());
      7 |         println!("在内层使用 result:{}", result);
      8 |     }
      9 | }
  编译:成功
  运行(退出码 0):
    在内层使用 result:long string

多个生命周期参数

实测 · 本机执行输出
  源码:
      1 | fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str { let _ = x; y }
      2 | fn main() { println!("{}", first("a", "b")); }
  编译:失败
    error: lifetime may not live long enough
     --> <实验目录>/src/u23e.rs:1:66
      |
    1 | fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str { let _ = x; y }
      |          --  -- lifetime `'b` defined here                       ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
      |          |
      |          lifetime `'a` defined here
      |
      = help: consider adding the following bound: `'b: 'a`
    
    error: aborting due to 1 previous error
  源码:
      1 | fn first<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x }
      2 | fn main() {
      3 |     let a = String::from("long");
      4 |     let r;
      5 |     { let b = String::from("short"); r = first(&a, &b); }   // 返回值只与 a 相关,b 先死也没关系
      6 |     println!("{r}");
      7 | }
  编译:成功
  运行(退出码 0):
    long

运行时不存在

实测 · 本机执行输出
  elided 与 explicit 共同的汇编: add	x0, x0, #4 ; ret
  别名行: `_explicit = _elided`(LLVM 合并了两个相同的函数)
  size_of::<&u32>() 在有无生命周期标注时都是 8 字节;生命周期只在编译期存在

省略规则见生命周期省略规则;函数返回引用的具体情形见函数返回引用的生命周期。

分步演示

生命周期标注
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。汇编目标 aarch64-apple-darwin
    • 没有验证:高阶生命周期(for<'a>)、生命周期子类型的更多形式、struct 里的生命周期(见后面的文章)、'static 约束
    京ICP备2026067502号-1京公网安备11010502063487号