此代码定义了一个非常简单的特征,用于表示二叉树和实现该特征的结构:
pub trait BTree<T> {
fn all(&self) -> Option<(&Self, &Self, &T)>;
fn left(&self) -> Option<&Self>;
fn right(&self) -> Option<&Self>;
fn value(&self) -> Option<&T>;
}
pub struct MyBTree<T> {
opt: Option<Box<(MyBTree<T>, MyBTree<T>, T)>>,
}
impl<T> BTree<T> for MyBTree<T> {
fn all(&self) -> Option<(&Self, &Self, &T)> {
match self.opt {
None => None,
Some(ref tuple) => Some((&tuple.0, &tuple.1, &tuple.2)),
}
}
fn left(&self) -> Option<&Self> {
match self.all() {
None => None,
Some((left, _, _)) => Some(left),
}
}
fn right(&self) -> Option<&Self> {
match self.all() {
None => None,
Some((right, _, _)) => Some(right),
}
}
fn value(&self) -> Option<&T> {
match self.all() {
None => None,
Some((_, _, value)) => Some(value),
}
}
}
left
, right
和 value
的实现可以在特征内部移动,因为它们仅依赖于特征定义的 all
方法,而不依赖于实现细节 .
这适用于 value
,但不适用于 left
和 right
. 例如,如果我尝试在特征体中移动 left
的实现,则会出现以下编译错误:
error[E0311]: the parameter type `T` may not live long enough
--> src/lib.rs:6:24
|
6 | match self.all() {
| ^^^
|
= help: consider adding an explicit lifetime bound for `T`
note: the parameter type `T` must be valid for the anonymous lifetime #1 defined on the method body at 5:9...
--> src/lib.rs:5:9
|
5 | / fn left(&self) -> Option<&Self> {
6 | | match self.all() {
7 | | None => None,
8 | | Some((left, _, _)) => Some(left),
9 | | }
10| | }
| |_________^
note: ...so that the reference type `&T` does not outlive the data it points at
--> src/lib.rs:6:24
|
6 | match self.all() {
|
为什么这个问题出现在特征中而不是在 MyBTree
的实现中?
为什么编译器会在忽略T值的方法中抱怨 T
的生命周期 - 虽然它适用于在返回类型中提到T的方法 value
?
1 回答
当您考虑为具有生命周期的类型实现
BTree<T>
时,这些方法签名会变得更加细微 . 我对涉及泛型类型参数或Self
类型的所有生命周期错误的一般建议是:关注类型为借用类型的情况 .借用类型的问题是,您永远不会拥有比其引用的数据更长的生命周期的引用 . 这个原则最简单的例子是:
Rust强制我们保证引用引用的数据超过引用,在这种情况下
'b
outlives'a
.现在让我们通过考虑如果
T
是借用类型&()
会出现什么问题来将此应用于您的BTree
情况 . 首先,查看您在impl<T> BTree<T> for MyBTree<T>
中放置的以下两种方法 . 我明确地写了明确的生命时间来澄清讨论 .为了让调用者调用
left
,他们 must 知道Self
超过了生命'a
. 并且为了让调用者能够调用value
,他们知道Self
的寿命超过终身'a
(为了使&'a T
成为一个有意义的类型,如上所述) . 除非满足这些要求,否则借用检查员不会让他们使用这些方法,因此实施可以假设满足这些要求 .此外,借阅检查器可以看到 if
Self
也超过了 and ,因为MyBTree<T>
包含T
类型的值 .这就是为什么在
impl<T> BTree<T> for MyBTree<T>
中实现left
和value
没有问题 . 呼叫者和MyBTree<T>
结构共同保证一切都在我们需要的时间生存 .现在我们在
BTree<T>
特征定义中有这些方法 .这里的事情出了问题,因为如果调用者调用了
left
,他们知道Self
已经超过了'a
,但是他们 have not guaranteed 这已经超过了'a
. 例如,对于一些完全不相关的较短寿命'b
,它们可以具有T=&'b ()
. 如上所述,这将使&'a T
等于&'a &'b ()
,这不是合法类型 .Rust对特征中定义的
value
感到满意的原因是调用者保证Self
和T
比输入生命周期延长'a
. Rust对特征中定义的left
不满意的原因是调用者只保证Self
outlives'a
,而不是T
outlives'a
,实现假设 .那么错误不是关于返回值,而是关于
all()
的调用 . 仔细看 .为了调用
all()
,调用者负责证明输入和输出类型是有效类型 . 但是在T
类似于&'b ()
的情况下,这可能不是真的 .all()
将返回&'a &'b ()
,因此借用检查器会阻止呼叫 .我们可以通过明确我们的实现所假设的保证来解决这个问题,在这种情况下
T
超过'a
.