考虑以下玩具示例:
filtered_profile
这不能编译。它声称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
我真的不明白这个错误。特别是“期望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
这是怎么回事?
答案 0 :(得分:17)
特质对象类型具有关联的生存期限制,但可以省略。完整特征对象类型写为dyn Trait + 'a
(在引用后面时,必须在其周围添加括号:&(dyn Trait + 'a)
)。
棘手的部分是,如果忽略生命周期限制,则the rules are a bit complicated。
首先,我们有:
impl PartialOrd for dyn SimpleOrder {
在这里,编译器会推断+ 'static
。终生参数永远不会在impl
块上引入(从Rust 1.32.0开始)。
接下来,我们有:
fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
推断other
的类型为&'b (dyn SimpleOrder + 'b)
,其中'b
是在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)
。有什么问题吗?
实际上,cmp
不会给出任何错误,因为其实现不需要两个特征对象的生存期相等。为什么partial_cmp
会在乎?
因为partial_cmp
正在呼叫Ord::cmp
。当类型检查对特征方法的调用时,编译器将检查来自特征的签名。让我们回顾一下该签名:
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
),因此编译器会给出错误消息。
那么,为什么在实现other
时将&'b (dyn SimpleOrder + 'b)
的类型设置为Ord
是有效的?因为&'b (dyn SimpleOrder + 'b)
是&'b (dyn SimpleOrder + 'static)
的{{3}},并且Rust允许您在实现trait方法时用其超类型之一替换参数类型(严格来说,该方法更加通用)显然在类型检查中用的很少。)
为了使实现尽可能通用,应在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 {}