问题描述
考虑以下玩具示例:
use std::cmp::Ordering;
pub trait SimpleOrder {
fn key(&self) -> u32;
}
impl PartialOrd for dyn SimpleOrder {
fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for dyn SimpleOrder {
fn cmp(&self, other: &dyn SimpleOrder) -> Ordering {
self.key().cmp(&other.key())
}
}
impl PartialEq for dyn SimpleOrder {
fn eq(&self, other: &dyn SimpleOrder) -> bool {
self.key() == other.key()
}
}
impl Eq for SimpleOrder {}
这不能编译.它声称在partial_cmp
的实现中存在生命周期问题:
This doesn't compile. It claims there is a lifetime issue in the implementation for partial_cmp
:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:9:23
|
9 | Some(self.cmp(other))
| ^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 8:5...
--> src/main.rs:8:5
|
8 | / fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
9 | | Some(self.cmp(other))
10| | }
| |_____^
note: ...so that the declared lifetime parameter bounds are satisfied
--> src/main.rs:9:23
|
9 | Some(self.cmp(other))
| ^^^^^
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:
expected std::cmp::Eq
found std::cmp::Eq
我真的不明白这个错误.尤其是发现std::cmp::Eq
的预期std::cmp::Eq
" 令人困惑.
I really don't understand this error. In particular "expected std::cmp::Eq
found std::cmp::Eq
" is puzzling.
如果我手动内联呼叫,则可以正常编译:
If I inline the call manually it compiles fine:
fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
Some(self.key().cmp(&other.key()))
}
这是怎么回事?
推荐答案
特质对象类型具有关联的生存期限制,但可以省略.完整特征对象类型写为dyn Trait + 'a
(在引用后面时,必须在引用后面加上括号:&(dyn Trait + 'a)
).
Trait object types have an associated lifetime bound, but it can be omitted. A full trait object type is written dyn Trait + 'a
(when behind a reference, parentheses must be added around it: &(dyn Trait + 'a)
).
棘手的部分是,当忽略生命周期限制时,规则有点复杂.
The tricky part is that when a lifetime bound is omitted, the rules are a bit complicated.
首先,我们有:
impl PartialOrd for dyn SimpleOrder {
在这里,编译器会推断+ 'static
.终身参数从未在impl
块上引入(从Rust 1.32.0开始).
Here, the compiler infers + 'static
. Lifetime parameters are never introduced on impl
blocks (as of Rust 1.32.0).
接下来,我们有:
fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
推断other
的类型为&'b (dyn SimpleOrder + 'b)
,其中'b
是在partial_cmp
上引入的隐式生命周期参数.
The type of other
is inferred to be &'b (dyn SimpleOrder + 'b)
, where 'b
is an implicit lifetime parameter introduced on partial_cmp
.
fn partial_cmp<'a, 'b>(&'a self, other: &'b (dyn SimpleOrder + 'b)) -> Option<Ordering> {
所以现在我们有了self
的类型是&'a (dyn SimpleOrder + 'static)
而other
的类型是&'b (dyn SimpleOrder + 'b)
.有什么问题吗?
So now we have that self
has type &'a (dyn SimpleOrder + 'static)
while other
has type &'b (dyn SimpleOrder + 'b)
. What's the problem?
的确,cmp
不会给出任何错误,因为其实现不需要两个特征对象的生存期相等.为什么partial_cmp
为何在乎?
Indeed, cmp
doesn't give any error, because its implementation doesn't require that the lifetime of the two trait objects be equal. Why does partial_cmp
care, though?
因为partial_cmp
正在调用Ord::cmp
.当类型检查对特征方法的调用时,编译器将检查来自特征的签名.让我们回顾一下该签名:
Because partial_cmp
is calling Ord::cmp
. When type checking a call to a trait method, the compiler checks against the signature from the trait. Let's review that signature:
pub trait Ord: Eq + PartialOrd<Self> {
fn cmp(&self, other: &Self) -> Ordering;
特征要求other
的类型为Self
.这意味着,当partial_cmp
调用cmp
时,它会尝试将&'b (dyn SimpleOrder + 'b)
传递给需要&'b (dyn SimpleOrder + 'static)
的参数,因为Self
是dyn SimpleOrder + 'static
.此转换无效('b
无法转换为'static
),因此编译器会给出错误.
The trait requires that other
be of type Self
. That means that when partial_cmp
calls cmp
, it tries to pass a &'b (dyn SimpleOrder + 'b)
to a parameter that expects a &'b (dyn SimpleOrder + 'static)
, because Self
is dyn SimpleOrder + 'static
. This conversion is not valid ('b
cannot be converted to 'static
), so the compiler gives an error.
那么,为什么在实现Ord
时将other
的类型设置为&'b (dyn SimpleOrder + 'b)
是有效的?因为&'b (dyn SimpleOrder + 'b)
是&'b (dyn SimpleOrder + 'static)
的超类型,而Rust使您可以替换实现特质方法时具有其超类型之一的参数类型(即使在类型检查中似乎不多使用它,它也使该方法更加严格地通用).
So then, why is it valid to set the type of other
to &'b (dyn SimpleOrder + 'b)
when implementing Ord
? Because &'b (dyn SimpleOrder + 'b)
is a supertype of &'b (dyn SimpleOrder + 'static)
, and Rust lets you replace a parameter type with one of its supertypes when implementing a trait method (it makes the method strictly more general, even though it's apparently not used much in type checking).
为了使您的实现尽可能通用,您应该在impl
s上引入一个生命周期参数:
In order to make your implementation as generic as possible, you should introduce a lifetime parameter on the impl
s:
use std::cmp::Ordering;
pub trait SimpleOrder {
fn key(&self) -> u32;
}
impl<'a> PartialOrd for dyn SimpleOrder + 'a {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for dyn SimpleOrder + 'a {
fn cmp(&self, other: &Self) -> Ordering {
self.key().cmp(&other.key())
}
}
impl<'a> PartialEq for dyn SimpleOrder + 'a {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
impl<'a> Eq for dyn SimpleOrder + 'a {}
这篇关于实现dyn对象的特征时出现神秘的生命周期问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!