While writing a library, I stumbled across an issue, which can be reproduced by the following minimal example:
struct T;
fn foo<'a>(a : impl Iterator<Item=&'a T>, b : &'a T) {
_ = (a, b);
}
fn bar<'a>(a : impl Iterator<Item=&'a T>) {
let b = T;
// Compiler complains here that &b doesn't life long enough (requires 'a).
foo(a, &b);
}
fn main() {
let a = vec![T, T];
bar(a.iter());
}
This code does not compile due to the reason stated in the comment.
But I don't understand why. My understanding would be that 'a of foo is choosen to be the shorter of the two lifetimes (lifetime of b) and should compile fine.
associated types are invariant, so the lifetime in impl Iterator<Item = &'a T> cannot be shortened.
EDIT:
forget to mention, for this particular example, the solution is to manually "shorten" the lifetime:
fn bar<'a>(a: impl Iterator<Item = &'a T>, b: &'a T) {
let b = T;
foo(a.map(|x| x), &b);
}
your actual use case might be more complex, but the idea is similar, you must design the trait which can express variant explicitly like an reborrow() method and such.
yeah, I agree the reference should include this case, at least a short conclusion should be available.
this behavior is actually documented in the rustc development guide, but it's a little bit technical:
an intuitive reason why associated type must be invariant in general can be demonstrated with this example:
trait Foo {
type Bar;
}
impl<'a> Foo for &'a i16 {
type Bar = &'a ();
}
impl<'a> Foo for &'a i32 {
type Bar = &'a mut &'a mut ();
}
impl<'a> Foo for &'a i64 {
type Bar = fn(&'a mut ());
}
type CoV<'a> = <&'a i16 as Foo>::Bar;
type InV<'a> = <&'a i32 as Foo>::Bar;
type ContraV<'a> = <&'a i64 as Foo>::Bar;
as you can see, in a generic context T: Foo, there's no way to know the variant of <T as Foo>::Bar, so the compiler must assume invariant in all cases.