Rust 生命周期

为什么需要标注、`'a` 怎么写、省略规则、以及 `'static`。

Advanced
Key Points生命周期标注不改变存活时间,只是告诉编译器「这些引用之间的相对关系」。

为什么需要

Rust
// 编译错误:missing lifetime specifier
// fn longest(x: &str, y: &str) -> &str {
//     if x.len() > y.len() { x } else { y }
// }

// 正确:告诉编译器返回值活得和两个参数一样久
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let a = String::from("long string");
    let r;
    {
        let b = String::from("xyz");
        r = longest(&a, &b);
        println!("{}", r);
    }
    // println!("{}", r);   // 编译错误:b 已经释放,r 可能指向它
}
Output
long string

The compiler cannot tell whether the return borrows from x or y, so it cannot know how long it lives. 'a means "the return lives no longer than the shorter of the two".

生命周期不改变寿命

Rust
// 这个标注毫无作用:'a 只约束关系,不延长任何东西
fn bad<'a>(x: &'a str) -> &'a str {
    x
}

// 这个编译不过:返回值引用了局部变量
// fn worse<'a>() -> &'a str {
//     let s = String::from("x");
//     &s
// }

Many assume adding 'a relaxes the compiler. The opposite: annotations are promises, and more promises mean fewer accepted programs. That is why most functions need none.

省略规则

Rust
// 下面三个签名等价,编译器自动补全
fn f1(s: &str) -> &str { s }
fn f2<'a>(s: &'a str) -> &'a str { s }
fn f3<'a>(s: &'a str) -> &'a str { s }
RuleMeaning
1每个输入引用各得一个独立生命周期
2只有一个输入引用时,输出沿用它的
3有 &self / &mut self 时,输出沿用 self 的
Rust
// 方法里最常见:返回 &self 的一部分,不用写生命周期
struct Parser {
    input: String,
}

impl Parser {
    fn first_word(&self) -> &str {
        self.input.split_whitespace().next().unwrap_or("")
    }
}

'static

Rust
// 字符串字面量就是 &'static str
let s: &'static str = "整个程序期间有效";

// 拥有所有权的类型隐含 'static
fn need_static<T: 'static>(v: T) { }

need_static(String::from("ok"));
need_static(42);

// 常见于线程
std::thread::spawn(|| {
    let x: &'static str = "hello";
    println!("{}", x);
});
'static does not mean "leaks". String satisfies T: 'static because it contains no short-lived references, not because it never frees. That misunderstanding makes async 'static bounds look scarier than they are.

结构体里的生命周期

Rust
// 结构体持有引用时必须标生命周期
struct Excerpt<'a> {
    part: &'a str,
}

impl<'a> Excerpt<'a> {
    fn announce(&self) -> &str {
        self.part
    }
}

fn main() {
    let novel = String::from("从前有座山。山里有座庙。");
    let first = novel.split('。').next().unwrap();

    let e = Excerpt { part: first };
    println!("{}", e.announce());
}
Output
从前有座山

If a struct must hold data long-term, store String instead of a reference. "Own it or borrow it, do not mix" is the most practical advice for avoiding lifetime hell.

Friends & Partners