在Rust中,结构体可以通过在结构体定义中指定显式生命周期(lifetimes)来存储对其他对象的引用。这是为了使编译器能够检查没有引用会变得无效。以下是一个例子:
struct Book<'a> { title: &'a str, author: &'a str, }
这里的结构体Book包含两个具有相同生命周期'a的引用。当你编写一个返回这样的结构体的函数时,你必须确保它其中的所有引用在函数的内部逻辑之外都是有效的:
fn book_factory<'a>(title: &'a str, author: &'a str) -> Book<'a> { Book { title, author } }
如果尝试从函数返回一个结构体,其中字段引用指向例如该函数的局部变量,则会发生编译错误,因为引用的生命周期不足以进行安全访问。
可以创建一个结构体,其中一个字段包含引用(&str),而另一个字段包含String吗?为什么这可能是个问题?
常见的错误答案是:“是的,可以,这很安全”。
实际上,如果&str是从String获得的,而结构体的生命周期超出了这个String,该引用将变成悬垂引用(dangling reference)。例如:
struct Test<'a> { s1: &'a str, s2: String, } fn main() { let s = String::from("hello"); let t = Test { s1: &s, s2: s }; // t.s1在实际上是安全的,只有当s2(s)存活时,但如果s2先被删除 — 错误 }
故事 在一个项目中,尝试从加载函数返回包含指向在同一函数内部创建的临时字符串的结构体。代码无法编译,需要大量重写以消除局部变量的生命周期。
故事 开发者打算使用一个包含指向在函数中创建的数组元素的引用的结构体,但在返回后,该引用变得无效,编译器成功地防止了这种情况。
故事 在一个企业项目中,开发人员存储了对从集合中移除的另一个对象的字符串的引用&str,这个对象在引用之前就已被删除 — 在后续访问该引用时发生了panic。