The test code of some standard library containers asserts the (co)variance of some iterators. For BTreeMap (and BTreeSet), it also test the container itself. For instance, it checks that something like this compiles:
fn test_variance<'new>(v: BTreeSet<&'static str>) -> BTreeSet<&'new str> {
v
}
I read this as "if you carry long lived references, you can pretend to be carrying short lived references", and I gather that this is what covariance boils down to in Rust.
I'm trying to understand in what code this comes into play. I thought this would be a good example:
fn pick_from<'a>(set1: BTreeSet<&'a i32>, _set2: BTreeSet<&'a i32>) -> &'a i32 {
set1.iter().next().unwrap()
}
This function requires that both sets can pretend to have the same type and the same lifetime for their contents, right? So pass in two sets with references of different lifetimes, and we see covariance in action, right?
let one = 1;
let set1 = once(&one).collect::<BTreeSet<_>>();
{
let two = 2;
let set2 = once(&two).collect::<BTreeSet<_>>();
let _x = pick_from(set1, set2);
}
Well, no. if I go break the implementation of BTreeSet such that it is invariant (as far as I know) and fails test_variance
but succeeds all the rest, the pick_from
example also still happily compiles.
I wish I could find a simpler way to show that, but I couldn't figure out how to make a simple invariant container (variations of struct VecRef<'a, T>(Vec<&'a mut T>)
). Or at least something failing the test_variance
tests.