【问题标题】:为什么生命周期强制适用于结构而不适用于特征?
【发布时间】:2022-01-22 07:17:30
【问题描述】:

编译器不接受以下代码:

struct Struct<'a, 'b: 'a> {
    value: &'a dyn Value<'b>,
}

impl<'a, 'b: 'a> Struct<'a, 'b> {
    fn foo(&self) {
        UnitedLifetime(self.value);
    }
}

struct UnitedLifetime<'a>(&'a dyn Value<'a>);

trait Value<'a> {}

它会产生以下错误:

error[E0495]: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
 --> src/lib.rs:7:24
  |
7 |         UnitedLifetime(self.value);
  |                        ^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
 --> src/lib.rs:5:6
  |
5 | impl<'a, 'b: 'a> Struct<'a, 'b> {
  |      ^^
note: ...so that reference does not outlive borrowed content
 --> src/lib.rs:7:24
  |
7 |         UnitedLifetime(self.value);
  |                        ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'b` as defined here...
 --> src/lib.rs:5:10
  |
5 | impl<'a, 'b: 'a> Struct<'a, 'b> {
  |          ^^
note: ...so that the types are compatible
 --> src/lib.rs:7:24
  |
7 |         UnitedLifetime(self.value);
  |                        ^^^^^^^^^^
  = note: expected `dyn Value<'_>`
             found `dyn Value<'b>`

如果我使用 struct Value&lt;'a&gt;(&amp;'a u8); 而不是特征,它会编译。

我想要一个UnitedLifetime,它的生命周期与Struct 一样长,而它又具有其他对象的生命周期,这意味着'a: '_'b: '_ 始终为真。

我做错了什么?这是编译器错误吗?

【问题讨论】:

    标签: struct rust traits lifetime coercion


    【解决方案1】:

    原因是差异。

    dyn Trait&lt;'b&gt;'b 上是不变的。换句话说,这意味着'b 必须完全是'b,而不是任何其他生命周期,既不短也不长。因此,编译器不能使用“'a'b 中的较短者”。

    Trait objects are always invariant over their generic arguments.

    另一方面,u8 没有引用 'b&amp;'a u8'a 是协变的。这意味着它可以缩小,因此编译器可以使用“较短的”。

    如果您将dyn Value&lt;'b&gt; 替换为在'b 上不变的结构,您可以看到差异是问题而不是特征:

    struct Value<'a> {
        // `fn(&'a ()) -> &'a ()` is both covariant over `'a` (because of the parameter)
        // and contravariant over it (because of the return type). covariant+contravariant=invariant.
        _marker: PhantomData<fn(&'a ()) -> &'a ()>
    }
    

    Playground.


    要解释为什么特征对象在其参数上是不变的,请查看以下示例:

    trait Value<'a> {
        fn set_value(&mut self, v: &'a i32);
    }
    
    struct S<'a> {
        value: Box<&'a i32>,
    }
    
    impl<'a> Value<'a> for S<'a> {
        fn set_value(&mut self, v: &'a i32) {
            *self.value = v;
        }
    }
    
    fn foo<'a>(v: &mut dyn Value<'a>) {
        let new_value: i32 = 0;
        let new_value_ref: &i32 = &new_value;
        v.set_value(new_value_ref);
    }
    
    fn main() {
        let mut s = S { value: Box::new(&0) };
        foo(&mut s);
        dbg!(&**s.value);
    }
    

    Playground.

    如果特征对象在其参数上是协变的,foo() 将编译。我们只缩短生命周期 - 从 'a 到局部变量 new_value 的生命周期,它总是更短。

    但这会很糟糕,因为在退出 foo() 后,new_value 将被销毁,而 s.value 将指向释放的内存 - 释放后使用!

    这就是为什么可变引用是不变的。但是 trait 对象可以包含 任何东西 - 包括可变引用 - 所以它们的任何参数都必须是不变的!

    注意:我说如果我们对'a 是协变的,我们将编译,因为我们仍然采用&amp;mut self 并因此在S&lt;'a&gt; 上保持不变,并且传递性地,超过'a。但是我们可以调整这个例子以使用内部可变性并采用协变的&amp;self。我没有这样做是为了不让事情变得更复杂。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-08-22
      • 1970-01-01
      • 2019-02-24
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 2014-07-16
      相关资源
      最近更新 更多