如何使从对象到其孙子的生命周期约束 "pass through"?
How to make a lifetime constraint "pass through" from object to its grandchild?
我正在对生命周期和借用检查器进行一些实验。想象一下第一个结构:
struct First {}
impl First {
fn new() -> Self {
Self {}
}
fn second(&self) -> Second {
Second::new(self)
}
fn hello(&self) {
println!("Hello");
}
}
第二个,其生命周期限制取决于 First
:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
上面的代码工作得很好:Second
是由 First
创建的,它不能超过 First
。
问题
现在让我们修改 Second
以便它可以创建第三个结构,Third
:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
和Third
本身,这也取决于First
:
struct Third<'a> {
owner: &'a First,
}
impl<'a> Third<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
我想,在创建 Third
的实例时,它会依赖于 First
,但事实并非如此。实际上 Third
取决于 Second
:
fn main() {
let f = First::new();
let t = {
let sss = f.second();
sss.third() // error: sss does not live long enough
};
}
那么,我怎样才能使生命周期约束从 First
“通过”到 Third
?
创建Third
时,只需要在Second
中指定与内部引用相同的生命周期即可:
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third<'a> {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
和第一个创建Second
时一样:
impl First {
...
fn second(&self) -> Second {
Second::new(self)
}
...
}
在这里,second 被绑定到 'self
生命周期,也就是说,它可以与 &self
First
实例一样长。但在这种情况下,编译器会为您指定它。
Rust 有如何推断函数生命周期的规则:lifetime elision rules.
这些规则规定:
- Each elided lifetime (i.e. a type that should had have a lifetime, but doesn't, like
&T
that is actually &'a T
) in the parameters becomes a distinct lifetime parameter.
- If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.
In method signatures there is another rule
- If the receiver has type
&Self
or &mut Self
, then the lifetime of that reference to Self
is assigned to all elided output lifetime parameters.
我们以First::second()
为例。它的签名是:
fn second(&self) -> Second
或者,显式省略所有生命周期(顺便说一下,显式省略所有不在引用上的生命周期被认为是一种很好的做法,例如本例中的 Second<'_>
):
fn second(&'_ self) -> Second<'_>
所以根据规则 #1 我们分配一个新的生命周期,我们称它为 'a
,到 &self
:
fn second<'a>(&'a self) -> Second<'_>
现在,根据规则 #3,我们为 Second<'_>
选择 'a
:
fn second<'a>(&'a self) -> Second<'a>
也就是说,我们 return Second
与对 self
的引用具有相同的生命周期。
现在让我们将它应用到 Second::third()
...
fn third(&self) -> Third
// Becomes
fn third<'b>(&'b self) -> Third<'b> // Lifetime `'a` is already used
但这不是我们想要的!我们希望生成的 Third
取决于我们包含的 First
实例的生命周期,而不是 &self
的生命周期!所以我们真正需要的是使用 Third<'a>
:
fn third(&self) -> Third<'a> { ... }
我正在对生命周期和借用检查器进行一些实验。想象一下第一个结构:
struct First {}
impl First {
fn new() -> Self {
Self {}
}
fn second(&self) -> Second {
Second::new(self)
}
fn hello(&self) {
println!("Hello");
}
}
第二个,其生命周期限制取决于 First
:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
上面的代码工作得很好:Second
是由 First
创建的,它不能超过 First
。
问题
现在让我们修改 Second
以便它可以创建第三个结构,Third
:
struct Second<'a> {
owner: &'a First,
}
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
和Third
本身,这也取决于First
:
struct Third<'a> {
owner: &'a First,
}
impl<'a> Third<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn hello(&self) {
self.owner.hello();
}
}
我想,在创建 Third
的实例时,它会依赖于 First
,但事实并非如此。实际上 Third
取决于 Second
:
fn main() {
let f = First::new();
let t = {
let sss = f.second();
sss.third() // error: sss does not live long enough
};
}
那么,我怎样才能使生命周期约束从 First
“通过”到 Third
?
创建Third
时,只需要在Second
中指定与内部引用相同的生命周期即可:
impl<'a> Second<'a> {
fn new(owner: &'a First) -> Self {
Self { owner }
}
fn third(&self) -> Third<'a> {
Third::new(self.owner)
}
fn hello(&self) {
self.owner.hello();
}
}
和第一个创建Second
时一样:
impl First {
...
fn second(&self) -> Second {
Second::new(self)
}
...
}
在这里,second 被绑定到 'self
生命周期,也就是说,它可以与 &self
First
实例一样长。但在这种情况下,编译器会为您指定它。
Rust 有如何推断函数生命周期的规则:lifetime elision rules.
这些规则规定:
- Each elided lifetime (i.e. a type that should had have a lifetime, but doesn't, like
&T
that is actually&'a T
) in the parameters becomes a distinct lifetime parameter.- If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.
In method signatures there is another rule
- If the receiver has type
&Self
or&mut Self
, then the lifetime of that reference toSelf
is assigned to all elided output lifetime parameters.
我们以First::second()
为例。它的签名是:
fn second(&self) -> Second
或者,显式省略所有生命周期(顺便说一下,显式省略所有不在引用上的生命周期被认为是一种很好的做法,例如本例中的 Second<'_>
):
fn second(&'_ self) -> Second<'_>
所以根据规则 #1 我们分配一个新的生命周期,我们称它为 'a
,到 &self
:
fn second<'a>(&'a self) -> Second<'_>
现在,根据规则 #3,我们为 Second<'_>
选择 'a
:
fn second<'a>(&'a self) -> Second<'a>
也就是说,我们 return Second
与对 self
的引用具有相同的生命周期。
现在让我们将它应用到 Second::third()
...
fn third(&self) -> Third
// Becomes
fn third<'b>(&'b self) -> Third<'b> // Lifetime `'a` is already used
但这不是我们想要的!我们希望生成的 Third
取决于我们包含的 First
实例的生命周期,而不是 &self
的生命周期!所以我们真正需要的是使用 Third<'a>
:
fn third(&self) -> Third<'a> { ... }