struct - 为什么生命周期强制转换适用于结构而不适用于特征?

标签 struct rust traits lifetime coercion

编译器不接受以下代码:

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<'a>(&'a u8);它不是特征,而是编译。

我想要一个UnitedLifetime其生命周期长达 Struct ,它又具有其他对象的生命周期,这意味着 'a: '_'b: '_总是正确的。

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

最佳答案

原因是方差。

dyn Trait<'b>'b 上保持不变。换句话说,这意味着 'b必须正好是'b而不是任何其他的一生,既不短也不长。因此,编译器不能使用“'a'b 中较短的一个”。

Trait objects are always invariant over their generic arguments .

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

如果您替换 dyn Value<'b>,您会发现方差是问题而不是特征。具有在 '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将指向已释放的内存 - 释放后使用!

这就是可变引用不变的原因。但是特征对象可以包含任何东西 - 包括可变引用 - 因此它们的任何参数都必须是不变的!

注意:当我说我们是否会在 'a 上协变时,我并不准确。我们将编译,因为我们仍然需要 &mut self因此在 S<'a> 上保持不变并且,传递性地超过 'a 。但我们可以调整此示例以使用内部可变性并采用 &self这是协变的。我这样做并不是为了让事情变得更加复杂。

关于struct - 为什么生命周期强制转换适用于结构而不适用于特征?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70425773/

相关文章:

amazon-web-services - 即使存在也缺少 key

mongodb - 使用 mongodb-1.2.2 和 Rocket-0.5.0-rc.1 时如何解决异步不兼容问题?

scala - 为什么 Scala API 有两种组织类型的策略?

c++ - 类型特征以识别可以以二进制形式读/写的类型

python - actix Actor怎么会有PyO3 Python?

c++ - 类型特征检查 OF CRTP 派生,在基类中,问题是未定义的类型

c++ - 结构 vector push_back C++

c - 在C中为结构指针数组成员分配地址

用作结构字段或结构方法

rust - Rust 有没有一种简单的方法来计算 bool 值?