考虑下面的玩具例子:

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

我真的不明白这个错误.尤其令人费解.

如果我手动内联调用,它可以很好地编译:

fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
    Some(self.key().cmp(&other.key()))
}

这是怎么回事?

推荐答案

特征对象类型有一个关联的生存期界限,但可以忽略它.完整的trait对象类型写为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),其中'bpartial_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不会给出任何错误,因为它的实现不要求两个trait对象的生存期相等.但为什么partial_cmp会在意呢?

因为partial_cmpOrd::cmp.当类型判断对trait方法的调用时,编译器会判断trait的签名.让我们回顾一下这个签名:

pub trait Ord: Eq + PartialOrd<Self> {
    fn cmp(&self, other: &Self) -> Ordering;

这种trait 要求other人属于Self型.这意味着,当partial_cmp调用cmp时,它试图将&'b (dyn SimpleOrder + 'b)传递给期望&'b (dyn SimpleOrder + 'static)的参数,因为Selfdyn SimpleOrder + 'static.此转换无效('b无法转换为'static),因此编译器会给出一个错误.

那么,为什么在实现Ord时将other的类型设置为&'b (dyn SimpleOrder + 'b)是有效的呢?因为&'b (dyn SimpleOrder + 'b)&'b (dyn SimpleOrder + 'static)中的supertype,Rust允许你在实现一个trait方法时用它的一个超类型替换一个参数类型(它使这个方法更加通用,尽管它在类型判断中显然没有太多使用).


为了使您的实现尽可能通用,您应该在impls上引入一个生存期参数:

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 {}

Rust相关问答推荐

生成的future 的 struct 是什么?

在泛型 struct 的字段声明中访问关联的Conant

有没有办法模仿对象安全克隆?

函数内模块的父作用域的访问类型

如果变量本身不是None,如何返回;如果没有,则返回None&Quot;?

为相同特征的特征对象使用 move 方法实现特征

如何从宏调用闭包?

在 Rust 中,是否可以定义一个需要实现类型的构造函数的对象安全特征?

可选包装枚举的反序列化

`use` 和 `crate` 关键字在 Rust 项目中效果不佳

我可以禁用发布模式的开发依赖功能吗?

如何基于常量在Rust中跳过一个测试

不安全块不返回预期值

在运行时在 Rust 中加载字体

LinkedList::drain_filter::drop 中 DropGuard 的作用是什么?

深度嵌套枚举的清洁匹配臂

制作嵌套迭代器的迭代器

如何断言代码不会在测试中编译?

如果我立即等待,为什么 `tokio::spawn` 需要一个 `'static` 生命周期?

为什么这个 Trait 无效?以及改用什么签名?