为什么函数体在结构中编译,而不是在特征中?
Why does a function body compile in a struct, but not in a trait?
这段代码定义了一个非常简单的特征来表示二叉树和一个实现该特征的结构:
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
的生命周期——而它与方法 value
一起工作哪个 在其 return 类型中提到了 T?
How come this problem occurs in the trait, but not in its implementation for 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
成为有意义的类型,正如我们在上面看到的)。除非满足这些要求,否则借用检查器不会让他们调用这些方法,因此实现可以假定满足这些要求。
此外,借用检查器可以看到 if Self
比 'a
then 还 T
比 'a
长,因为 MyBTree<T>
包含类型 T
.
的值
这就是为什么在 impl<T> BTree<T> for MyBTree<T>
中实施 left
和 value
没有问题。调用者和 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
长,但他们 无法保证 T
比 'a
长寿。例如,对于一些完全不相关的较短生命周期 'b
,它们可能有 T=&'b ()
。正如我们在上面看到的,这将使 &'a T
等于 &'a &'b ()
这不是合法类型。
Rust 对特征中定义的 value
满意的原因是调用者保证 Self
和 T
比输入生命周期 'a
长。 Rust 对特征中定义的 left
不满意的原因是调用者只保证 Self
比 'a
长,而不是 T
比实现假设的 'a
长.
And how come the compiler complains about the lifetime of T in the methods who ignore the T value -- while it works with the method value
which does mention T in its return type?
嗯,错误与 return 值无关,而是与对 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()
会 return &'a &'b ()
所以借用检查器会阻止调用。
我们可以通过明确我们的实现假设的保证来解决这个问题,在这种情况下 T
比 'a
长。
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self>
where
T: 'a,
{
/* ... */
}
}
这段代码定义了一个非常简单的特征来表示二叉树和一个实现该特征的结构:
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
的生命周期——而它与方法 value
一起工作哪个 在其 return 类型中提到了 T?
How come this problem occurs in the trait, but not in its implementation for 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
成为有意义的类型,正如我们在上面看到的)。除非满足这些要求,否则借用检查器不会让他们调用这些方法,因此实现可以假定满足这些要求。
此外,借用检查器可以看到 if Self
比 'a
then 还 T
比 'a
长,因为 MyBTree<T>
包含类型 T
.
这就是为什么在 impl<T> BTree<T> for MyBTree<T>
中实施 left
和 value
没有问题。调用者和 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
长,但他们 无法保证 T
比 'a
长寿。例如,对于一些完全不相关的较短生命周期 'b
,它们可能有 T=&'b ()
。正如我们在上面看到的,这将使 &'a T
等于 &'a &'b ()
这不是合法类型。
Rust 对特征中定义的 value
满意的原因是调用者保证 Self
和 T
比输入生命周期 'a
长。 Rust 对特征中定义的 left
不满意的原因是调用者只保证 Self
比 'a
长,而不是 T
比实现假设的 'a
长.
And how come the compiler complains about the lifetime of T in the methods who ignore the T value -- while it works with the method
value
which does mention T in its return type?
嗯,错误与 return 值无关,而是与对 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()
会 return &'a &'b ()
所以借用检查器会阻止调用。
我们可以通过明确我们的实现假设的保证来解决这个问题,在这种情况下 T
比 'a
长。
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self>
where
T: 'a,
{
/* ... */
}
}