楼层: 首页/ 软件技术/ Rust 语言基础/ 生命周期 Lifetime:引用的保质期
09

生命周期 Lifetime:引用的保质期

Lifetimes · Borrow Checker · 'a

生命周期是 Rust 独有的概念,也是新手最怕的一关。别被名字吓到——它就是"引用的保质期":编译器要保证你手里的引用,不会比它借的数据活得更久(否则就是悬垂引用)。好消息是大多数情况编译器自动推断,你根本不用写。

论什么时候才需要手写生命周期

当函数返回一个引用,而这个引用从多个参数里来的时候,编译器不知道该返回哪个参数的"保质期",才需要你手动标注 'a。就这一种典型场景。除此之外,省略规则基本都能搞定。

经典例子:longest

// 返回两个字符串切片中较长的那个 // 返回值 &str 到底借的是谁?编译器不知道,所以要标注 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let s1 = String::from("长字符串"); let result; { let s2 = String::from("短"); result = longest(&s1, &s2); println!("较长的是 {}", result); // 在 s2 还活着时用,OK } // println!("{}", result); // 放这就报错:s2 已 drop,result 悬垂 }

那个 <'a> 的意思是:x、y、返回值三者引用的数据,都活在同一个叫 'a 的有效期里,返回值的保质期等于两个参数里较短的那个。这就是编译器逼你"别返回一个马上就死的数据"。

省略规则:大多数情况不用写

论编译器的三条自动推断规则

①每个引用参数都有自己的生命周期。②只有一个输入生命周期时,输出的就用它。③如果有 &self/&mut self 方法,输出的生命周期就跟 self 走。三条走完还判不出来,才要你手动标。所以你看到的 95% 代码都没有 'a。

结构体持有引用:必须标注

// 结构体里存引用,必须告诉编译器它引用的数据活多久 struct ImportantExcerpt<'a> { part: &'a str, } fn main() { let novel = String::from("很久很久以前"); let excerpt = ImportantExcerpt { part: &novel[0..4] }; println!("{}", excerpt.part); }

'static 生命周期

&'static str 表示这个引用活满整个程序——字符串字面量就是 &'static str,因为它被编译进二进制、程序结束才释放。日常看到 &'static str 不用慌,就是字面量字符串。

看到 "borrowed value does not live long enough" 怎么办

意思是:你返回的引用指向的数据,在引用还在用的时候就被 drop 了。解决思路:别返回引用,改成返回拥有所有权的 String(return String 而不是 &str),或者把数据挪到外层活得更久的作用域。生命周期报错看着吓人,八成是"引用提前消失"这一类。

另外,Rust 2018 引入的 NLL(Non-Lexical Lifetimes,非词法生命周期)已经把借用规则放宽:引用的有效期到"最后一次使用"就结束,不用等到整个块结束,所以实际比教科书例子宽松得多。