在 Rust 中,生命周期定义了引用的作用域,以便编译器能够确保指针不会悬空(没有悬挂引用)。这使得在编译期间保证内存安全,而无需垃圾回收。
当您处理引用时,Rust 要求明确指定它们的生命周期,当编译器无法自行推断时。通常是通过语法 'a 来实现:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
在这里,两个参数和返回值具有相同的 lifetime,这保证了返回的引用不会比任何一个参数活得长。
生命周期并不改变数据的生命周期,而只是向编译器描述它。
可以返回对函数内部局部变量的引用吗?
不可以: 因为这样的变量在离开函数时会被销毁。示例:
fn foo() -> &String { // 编译错误! let s = String::from("hello"); &s } // 对 s 的引用变得无效
编译器不会允许编译此代码:它会保护您避免使用已销毁数据的引用。
故事
在团队中出现了很多内存泄漏,当函数偶然返回对局部缓冲区的引用时。这不起作用,只有编译器开始对生命周期发怒才拯救了我们。由于这类错误的频繁出现,制定了一条规则:如果函数处理复杂的数据结构和嵌套引用,则必须明确指定生命周期。
故事
在一个项目中,编写了用于缓存数据的泛型代码。在生命周期泛型参数设计不当时,出现了 "cannot infer lifetime" 的错误,无法推断缓存中存储的数据的生命周期。这导致了通过反复试验调整生命周期注释,直到决定将缓存和非缓存数据分到不同的结构中。
故事
其中一位同事试图实现一个连接池,使用对连接对象的引用,但没有考虑到连接的生命周期与池的生命周期不匹配。最终在释放连接后产生了悬挂引用,这只在综合测试阶段才被发现。之后,项目转向使用安全的包装(Arc<Mutex<T>>)。