本文介绍了实现dyn对象的特征时出现神秘的生命周期问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下玩具示例:

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)的参数,因为Selfdyn 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 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 {}

这篇关于实现dyn对象的特征时出现神秘的生命周期问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 11:21