编译器不接受以下代码:
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 ()>
}
要解释为什么特征对象在其参数上保持不变,请检查以下示例:
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);
}
如果特征对象在其参数上是协变的,foo()
会编译。我们只是缩短生命周期 - 从 'a
到局部变量的生命周期new_value
,它总是更短。
但这会很糟糕,因为退出 foo()
后, new_value
将被摧毁,并且s.value
将指向已释放的内存 - 释放后使用!
这就是可变引用不变的原因。但是特征对象可以包含任何东西 - 包括可变引用 - 因此它们的任何参数都必须是不变的!
注意:当我说我们是否会在 'a
上协变时,我并不准确。我们将编译,因为我们仍然需要 &mut self
因此在 S<'a>
上保持不变并且,传递性地超过 'a
。但我们可以调整此示例以使用内部可变性并采用 &self
这是协变的。我这样做并不是为了让事情变得更加复杂。
关于struct - 为什么生命周期强制转换适用于结构而不适用于特征?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70425773/