感谢@francis-gagné对另一个问题的精彩回答,我更加清晰地了解了变异如何工作。例如,包含引用的类型在其生命周期参数上是协变的,如下所示。
struct Foo<'a> (PhantomData<&'a str>);
/// Foo is covariant over its lifetime parameter
pub fn test_foo<'a:'b, 'b:'c, 'c>() {
let fa: Foo<'a> = Foo(PhantomData);
let fb: Foo<'b> = Foo(PhantomData);
let fc: Foo<'c> = Foo(PhantomData);
let v: Vec<Foo<'b>> = vec![fa, fb]; // fc is not accepted
}
另一方面,接受引用(或包含引用的类型)的函数相对于其生命周期参数是逆变的。
struct Bar<'a> (PhantomData<fn(&'a str)>);
/// Bar is contravariant over its lifetime parameter
pub fn test_bar<'a:'b, 'b:'c, 'c>() {
let ba: Bar<'a> = Bar(PhantomData);
let bb: Bar<'b> = Bar(PhantomData);
let bc: Bar<'c> = Bar(PhantomData);
let v: Vec<Bar<'b>> = vec![bb, bc]; // ba is not accepted
}
最后,带有生命周期参数的trait在其生命周期参数上是不变的。
pub trait Baz<'a> {}
impl<'a> Baz<'a> for () {}
/// Baz is invariant over its lifetime parameter
pub fn test_baz<'a:'b, 'b:'c, 'c>() {
let za: Box<dyn Baz<'a>> = Box::new(());
let zb: Box<dyn Baz<'b>> = Box::new(());
let zc: Box<dyn Baz<'c>> = Box::new(());
let v: Vec<Box<dyn Baz<'b>>> = vec![zb]; // za and zx are not accepted
}
这是有道理的,因为该特性可以由协变类型和逆变类型实现,如下所示。
impl<'a> Baz<'a> for Foo<'a> {}
impl<'a> Baz<'a> for Bar<'a> {}
我的问题是:我能否强制一个特质在其生命周期参数上具有协变性?我期望有一个标记特质,例如:
trait Baz<'a>: Covariant<'a> {}
这将使得使用逆变类型实现该特性成为非法,同时允许za
成为上面的test_baz
函数中v
向量的一员。
当然,能够执行相反的操作(强制使特性逆变)也可能很有用...