Thanks to @francis-gagné 's excellent answer to another question, I have a clearer view of how variance works. For example, a type containing a reference is covariant over its lifetime parameter, as demonstrated below.
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
}
On the other hand a function accepting a reference (or a type containing it) is contravariant over its lifetime parameter.
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
}
Finally, a trait with a lifetime parameter is invariant over its lifetime parameter.
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
}
That makes sense, because the trait could be implemented both by a covariant and a contravariant type, as illustrated below.
impl<'a> Baz<'a> for Foo<'a> {}
impl<'a> Baz<'a> for Bar<'a> {}
My question is: can I force a trait to be covariant over its lifetime parameter? I would expect a marker trait such as:
trait Baz<'a>: Covariant<'a> {}
that would make it illegal to implement that trait with a contravariant type, and allow za
to be a member of the vector v
in the test_baz
function above.
Of course, being able to do the opposite (force a trait to be contravariant) could be useful as well...
No.
You can express "a value that implements Baz<'x>
for any 'x
":
pub fn test_baz<'a:'b, 'b:'c, 'c>() {
let za: Box<dyn for<'x> Baz<'x>> = Box::new(());
let zb: Box<dyn for<'x> Baz<'x>> = Box::new(());
let zc: Box<dyn for<'x> Baz<'x>> = Box::new(());
let v: Vec<Box<dyn for<'x> Baz<'x>>> = vec![za, zb, zc];
}
But you can't (as of Rust 1.31) write Box<dyn for<'x: 'b> Baz<'x>>
, and even if you could, that syntax would only work for lifetimes; it wouldn't let you express covariance over type parameters.