这显然是完全安全的
对人类来说显而易见的东西对编译器来说并不总是显而易见的;有时编译器不如人类聪明(但它更加警惕!)。
在这种情况下,您的原始代码在启用non-lexical lifetimes 时编译:
#![feature(nll)]
struct Foo<'a> {
boo: Option<&'a mut String>,
}
fn main() {
let mut foo = Foo { boo: None };
{
let mut string = "Hello".to_string();
foo.boo = Some(&mut string);
foo.boo.unwrap().push_str(", I am foo!");
foo.boo = None;
} // string goes out of scope. foo does not reference string anymore
} // foo goes out of scope
这是 only 因为 foo 一旦无效(在 string 超出范围之后)就永远不会使用,不是 因为您将值设置为None。尝试在最内层范围之后打印出值仍然会导致错误。
是否有可能有一个结构包含对一个比结构更短的生命周期的值的引用?
Rust 借用系统的目的是为了确保持有引用的事物不会比被引用的项目寿命更长。
在非词汇生命周期之后
也许,只要您不使用不再有效的引用。这有效,例如:
#![feature(nll)]
struct Foo<'a> {
boo: Option<&'a mut String>,
}
fn main() {
let mut foo = Foo { boo: None };
// This lives less than `foo`
let mut string1 = "Hello".to_string();
foo.boo = Some(&mut string1);
// This lives less than both `foo` and `string1`!
let mut string2 = "Goodbye".to_string();
foo.boo = Some(&mut string2);
}
在非词法生命周期之前
没有。借用检查器不够聪明,无法告诉您在引用无效后不能/不使用该引用。太保守了。
在这种情况下,您会遇到这样一个事实,即生命周期被表示为类型的一部分。换句话说,通用生命周期参数 'a 已经被“填充”了一个具体的生命周期值,覆盖了 string 存在的行。但是,foo 的生命周期比这些行长,因此您会收到错误消息。
编译器不会查看您的代码执行的操作;一旦它看到你用那个特定的生命周期参数化它,它就是这样。
我通常的解决方法是将类型分成两部分,需要引用的部分和不需要引用的部分:
struct FooCore {
size: i32,
}
struct Foo<'a> {
core: FooCore,
boo: &'a mut String,
}
fn main() {
let core = FooCore { size: 42 };
let core = {
let mut string = "Hello".to_string();
let foo = Foo { core, boo: &mut string };
foo.boo.push_str(", I am foo!");
foo.core
}; // string goes out of scope. foo does not reference string anymore
} // foo goes out of scope
注意这如何消除了对Option 的需求——你的类型现在告诉你字符串是否存在。
另一种解决方案是在设置字符串时映射整个类型。在这种情况下,我们消耗整个变量并通过更改生命周期来更改类型:
struct Foo<'a> {
boo: Option<&'a mut String>,
}
impl<'a> Foo<'a> {
fn set<'b>(self, boo: &'b mut String) -> Foo<'b> {
Foo { boo: Some(boo) }
}
fn unset(self) -> Foo<'static> {
Foo { boo: None }
}
}
fn main() {
let foo = Foo { boo: None };
let foo = {
let mut string = "Hello".to_string();
let mut foo = foo.set(&mut string);
foo.boo.as_mut().unwrap().push_str(", I am foo!");
foo.unset()
}; // string goes out of scope. foo does not reference string anymore
} // foo goes out of scope