为什么生命周期强制对结构有效但对特征无效?
Why does lifetime coercion work with structs but not with traits?
编译器不接受以下代码:
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);
而不是 traits,它会编译。
我想要一个 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 对象在其参数上是不变的,请检查以下示例:
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
将指向释放的内存 - 释放后使用!
这就是可变引用不变的原因。但是 trait 对象可以包含 任何东西 - 包括可变引用 - 所以它们的任何参数都必须是不变的!
注意: 我说如果我们在 'a
上协变我们将编译,我是不准确的,因为我们仍然采用 &mut self
和因此在 S<'a>
上不变,并且在 'a
上传递不变。但是我们可以调整此示例以使用内部可变性并采用协变的 &self
。我没有这样做是为了不让事情变得更复杂。
编译器不接受以下代码:
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);
而不是 traits,它会编译。
我想要一个 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 对象在其参数上是不变的,请检查以下示例:
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
将指向释放的内存 - 释放后使用!
这就是可变引用不变的原因。但是 trait 对象可以包含 任何东西 - 包括可变引用 - 所以它们的任何参数都必须是不变的!
注意: 我说如果我们在 'a
上协变我们将编译,我是不准确的,因为我们仍然采用 &mut self
和因此在 S<'a>
上不变,并且在 'a
上传递不变。但是我们可以调整此示例以使用内部可变性并采用协变的 &self
。我没有这样做是为了不让事情变得更复杂。