此代码定义了一个非常简单的特征,用于表示二叉树和实现该特征的结构:
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
,但不适用<{1}}和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() {
|
的实现中?
为什么编译器会在忽略 T值的方法中抱怨MyBTree
的生命周期 - 同时它适用于执行的方法T
在返回类型中提及T?
答案 0 :(得分:11)
为什么这个问题出现在特征中,而不是在MyBTree的实现中呢?
当您考虑为具有生命周期的类型实现BTree<T>
时,这些方法签名会变得更加细微。我对涉及泛型类型参数或Self
类型的所有生命周期错误的一般建议是:关注类型为借用类型的情况。
借用类型的问题是,您永远不会拥有比其引用的数据更长的生命周期的引用。这个原则最简单的例子是:
fn f<'a, 'b>() {
// error[E0491]: in type `&'a &'b ()`, reference has a longer
// lifetime than the data it references
let _: &'a &'b ();
}
Rust强制我们保证引用所引用的数据超过引用,在这种情况下'b
比'a
更长。
fn f<'a, 'b: 'a>() {
let _: &'a &'b ();
}
现在,如果BTree
是T
这样的借用类型,请考虑出现问题,然后将其应用于&()
情况。首先,查看您在impl<T> BTree<T> for MyBTree<T>
中放置的以下两种方法。我明确地写了明确的生命时间来澄清讨论。
impl<T> BTree<T> for MyBTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self> { /* ... */ }
fn value<'a>(&'a self) -> Option<&'a T> { /* ... */ }
}
为了让调用者调用left
,他们必须知道Self
超过了生命期'a
。并且为了让调用者调用value
,他们必须知道Self
超过生命周期'a
和 T
生命周期'a
(以使&'a T
成为有意义的类型,如上所述)。借用检查器不会让他们调用这些方法,除非满足这些要求,因此实现可以假设满足这些要求。
此外,借阅检查器可以看到如果 Self
超过'a
然后,T
超过'a
因为MyBTree<T>
包含T
类型的值。
这就是在left
中实施value
和impl<T> BTree<T> for MyBTree<T>
没有问题的原因。呼叫者和MyBTree<T>
结构共同保证了所有内容的存在,只要我们需要。
现在我们在BTree<T>
特征定义中使用了这些方法。
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self> { /* ... */ }
fn value<'a>(&'a self) -> Option<&'a T> { /* ... */ }
}
此处出现问题,因为如果来电者调用left
,则必须知道Self
超过'a
,但他们无法保证1} strong> T
超过'a
。例如,对于一些完全不相关的较短生命期T=&'b ()
,他们可以'b
。如上所述,这会使&'a T
等于&'a &'b ()
,这不是合法类型。
Rust对特征中定义的value
感到满意的原因是调用者保证Self
和T
都比输入生命周期'a
更长。 Rust对特征中定义的left
不满意的原因是调用者仅保证Self
比事件'a
更长,而不是T
比实现假定的'a
更长
为什么编译器会在忽略 T值的方法中抱怨T的生命周期 - 同时它适用于在返回类型中提到T的方法
value
?
错误与返回值无关,而是与all()
的调用有关。仔细看。
error[E0311]: the parameter type `T` may not live long enough
--> src/lib.rs:6:24
|
6 | match self.all() {
| ^^^
为了调用all()
,调用者负责证明输入和输出类型是有效类型。但是,如果T
类似于&'b ()
,则可能不是这样。 all()
将返回&'a &'b ()
,因此借用检查程序会阻止呼叫。
我们可以通过明确保证我们的实施假设来解决这个问题,在这种情况下,T
比'a
更长。
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self>
where
T: 'a,
{
/* ... */
}
}