以下是我要存档的简化版本:
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
这显然是完全安全的,因为foo.boo
None
一旦string
超出范围,就会Document document = DocumentHelper.createDocument();
Element documentRoot = null;
Element created = null;
Node parentNode = null;
documentRoot = DocumentHelper.createElement();
parentNode = documentRoot.selectSingleNode("parentXPath");
Namespace ns = new Namespace("name","value");
created = ((Element) parentNode).addElement(new QName("elementName",ns1));
。
有没有办法告诉编译器?
答案 0 :(得分:3)
这显然是完全安全的
对于人类来说,显而易见的是编译器并不总是显而易见的;有时编译器并不像人类那样聪明(但它更加警惕!)。
在这种情况下,您的原始代码会在启用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
这只是 ,因为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
答案 1 :(得分:2)
Shepmaster的回答是完全正确的:你无法用生命周期表达这一点,这是一个编译时功能。但是,如果您尝试复制可以在托管语言中运行的内容,则可以使用reference counting在运行时强制执行安全性。
(安全通常的Rust记忆安全感。在安全的Rust中仍然可能出现恐慌和泄漏;这有很好的理由,但这是另一个问题的主题。)
这是一个例子(playground)。 Rc
指针不允许变异,因此我必须添加一层RefCell
来模仿问题中的代码。
use std::rc::{Rc,Weak};
use std::cell::RefCell;
struct Foo {
boo: Weak<RefCell<String>>,
}
fn main() {
let mut foo = Foo { boo: Weak::new() };
{
// create a string with a shorter lifetime than foo
let string = "Hello".to_string();
// move the string behind an Rc pointer
let rc1 = Rc::new(RefCell::new(string));
// weaken the pointer to store it in foo
foo.boo = Rc::downgrade(&rc1);
// accessing the string
let rc2 = foo.boo.upgrade().unwrap();
assert_eq!("Hello", *rc2.borrow());
// mutating the string
let rc3 = foo.boo.upgrade().unwrap();
rc3.borrow_mut().push_str(", I am foo!");
assert_eq!("Hello, I am foo!", *rc3.borrow());
} // rc1, rc2 and rc3 go out of scope and string is automatically dropped.
// foo.boo now refers to a dropped value and cannot be upgraded anymore.
assert!(foo.boo.upgrade().is_none());
}
请注意,在foo.boo
超出范围之前我没有必要重新分配string
,就像在您的示例中一样 - Weak
指针在最后一个时自动标记为无效现存的Rc
指针被删除。这是Rust的类型系统即使在放弃共享&
指针的强编译时保证之后仍然可以帮助您实施内存安全的一种方式。