代码之家  ›  专栏  ›  技术社区  ›  orlp

Dyn对象实现特性时的神秘生命问题

  •  18
  • orlp  · 技术社区  · 6 年前

    考虑以下玩具示例:

    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::情商 令人费解。

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

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

    这里发生了什么事?

    1 回复  |  直到 6 年前
        1
  •  17
  •   Francis Gagné    6 年前

    特征对象类型有关联的生存期限制,但可以忽略。写入完整特征对象类型 dyn Trait + 'a (引用后面必须加括号: &(dyn Trait + 'a) )

    最棘手的是,如果忽略了终身限制, the rules are a bit complicated .

    首先,我们有:

    impl PartialOrd for dyn SimpleOrder {
    

    这里,编译器推断 + 'static . 从未在上引入寿命参数 impl 块(从锈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) 虽然 其他 有类型 &'B(Dyn SimpleOrder+'B) . 怎么了?

    的确, cmp 不会给出任何错误,因为它的实现不要求两个特征对象的生存期相等。为什么 部分CMP 但是,小心吗?

    因为 部分CMP 正在呼叫 Ord::cmp . 当类型检查对trait方法的调用时,编译器检查来自trait的签名。让我们回顾一下这个签名:

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

    这种特性要求 其他 属于类型 Self . 这意味着当 部分CMP 电话 化学机械抛光 ,它试图通过 &'B(Dyn SimpleOrder+'B) 到需要 &'b (dyn SimpleOrder + 'static) ,因为 自我 dyn SimpleOrder + 'static . 此转换无效( 无法转换为 'static ,所以编译器给出了一个错误。

    那么,为什么设置 其他 &'B(Dyn SimpleOrder+'B) 实施时 Ord ?因为 &'B(Dyn SimpleOrder+'B) 是一个 supertype 属于 &'B(dyn simpleorder+'静态) Rust允许您在实现一个特征方法时用它的一个超类型替换参数类型(它使该方法更加通用,尽管它在类型检查中显然不太常用)。


    为了使实现尽可能通用,应该在 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 {}