我需要一些帮助来理解如何指定生命周期,以使Rust理解我正在try 做什么(或者如果不可能,为什么不).
首先,这是我的基本情况,它工作得很好.设想一个工厂类型Foo
,它创建Bar
,每个Bar
borrow 创建它的Foo
.borrow 的目的是,当Bar
被放下时,它可以自动操作创建它的Foo
.Foo
显然必须比Bar
活得更久,这正是我的意图,也是我希望编译器验证的东西.
struct Foo {
s: String,
i: u64,
}
struct Bar<'a> {
parent: Option<&'a mut Foo>,
s: String,
i: u64,
}
impl Foo {
fn new(s: String) -> Foo {
Foo { s, i: 0 }
}
fn push<'a>(&'a mut self, s: String) -> Bar<'a> {
Bar { parent: Some(self), s, i: 0 }
}
fn print(&self) {
println!("{}:{}", self.s, self.i)
}
}
impl<'a> Bar<'a> {
fn print(&self) {
println!("{}:{}", self.s, self.i)
}
}
impl<'a> Drop for Bar<'a> {
fn drop(&mut self) {
if let Some(parent) = &mut self.parent {
parent.i += 1;
}
}
}
fn main() {
let mut x = Foo::new("x".to_string());
{
let y = x.push("y".to_string());
y.print(); // y:0
} // when y is dropped it increments x.i
x.print(); // x:1
}
现在我希望做的是让这个模式递归,这样y
就可以递归地推送一个新的z
(其中y
必须像预期的那样比z
活得更久),依此类推.换句话说,我不希望有单独的Foo
和Bar
,而是希望有一个类型Foo
,它的push
方法创建了引用它的新Foo
.以下是我经过数小时的修修补补后得出的最好的建议:
struct Foo<'a, 'b> where 'b: 'a {
parent: Option<&'a mut Foo<'b, 'b>>, // note 1
s: String,
i: u64,
}
impl<'a, 'b> Foo<'a, 'b> {
fn new(s: String) -> Foo<'a, 'b> { // note 2
Foo { parent: None, s, i: 0 }
}
fn push(&'b mut self, s: String) -> Foo<'a, 'b> { // note 2
Foo { parent: Some(self), s, i: 0 }
}
fn print(&self) {
println!("{}:{}", self.s, self.i);
}
}
impl<'a, 'b> Drop for Foo<'a, 'b> {
fn drop(&mut self) {
if let Some(parent) = &mut self.parent {
parent.i += 1;
}
}
}
fn main() {
let mut x = Foo::new("x".to_string());
{
let y = x.push("y".to_string()); // error 1
y.print(); // y:0
} // when y is dropped it increments x.i
x.print(); // x:1, error 2
}
注意事项和错误详细信息:
-
Note 1:在
Foo<'b, 'b>
年中使用相同的生命周期两次是错误的,因为我并不是说Foo
的两个生命周期都需要匹配.然而,将'a
用于任何一个都似乎是错误的,如果不向Foo
添加另一个生命周期 参数,我就无法指定一个新的/不同的生命周期 ,这反过来又迫使我在这里指定第三个生命周期 ,这导致了同样的问题.想想看,它是递归的.只是为了好玩,我判断了Rust的C++可变模板的类似功能,但即使这是可能的,它仍然感觉像是一个粗略的解决方案. - Note 2这些行闻起来不对劲,因为我在返回类型中使用了生存期,而这些生存期没有出现在函数签名的其他地方,但我不确定该怎么办.
-
Error 1错误是"借来的价值生命周期 不够长"和"
x
掉在这里,还借着"("here"是最后一行).我的期望是y
对x
的引用只有y
那么长,但Rust显然不认为生存期是这样工作的,所以我似乎没有很好地告诉编译器应该如何约束(或不约束)生存期. -
Error 2这里的错误是"不能borrow
x
作为不可变的,因为它也被borrow 为可变的",这只是同一问题的另一个症状.我预计,当我们调用x.print()
时,位于y
内的x
的可变借入会消失,但是编译器没有.
当我寻找这个问题的解决方案时,我发现的很多东西都是关于创建循环引用的,其中父级有一个子级列表,每个子级都引用父级.我理解为什么这是困难/不可能的,但我不认为这是我在这里试图做的事情.我的父/工厂类型不维护对其子/小部件的引用;这些引用只有一个方向,从子/小部件到父/工厂.
我也发现自己阅读了铁 rust 规范中与此相关的部分,但老实说,我完全搞不懂.至少有一个关于生命如何实际工作的时刻我还没有经历过,这让我很难理解所有规范级别的总体细节,更不用说与我的问题相关的细节了.
我觉得我想让Rust做它擅长做的事情--验证父母的事情比子元素的事情更长久--但我不确定如何解释我试图对Rust做的事情,因为父母和子元素的事情具有相同的类型.有人能告诉我怎么做或者为什么不能做吗?