The following is reduced from the use of types from the tar
crate:
use std::marker::PhantomData;
use std::io::{self, Read};
use std::cell::RefCell;
struct Entries<'a>(PhantomData<&'a ()>);
struct Entry<'a>(PhantomData<&'a RefCell<dyn Read + 'a>>);
impl<'a> Iterator for Entries<'a> {
type Item = io::Result<Entry<'a>>;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
struct Foo<'a>(Entries<'a>);
impl<'a> Foo<'a> {
fn foo(&mut self) -> Option<Entry> {
self.0.next().transpose().unwrap()
}
}
It fails to compile with:
error: lifetime may not live long enough
--> src/lib.rs:21:9
|
19 | impl<'a> Foo<'a> {
| -- lifetime `'a` defined here
20 | fn foo(&mut self) -> Option<Entry> {
| - let's call the lifetime of this reference `'1`
21 | self.0.next().transpose().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
|
= note: requirement occurs because of the type `Entry<'_>`, which makes the generic argument `'_` invariant
= note: the struct `Entry<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
I don't quite understand what this all means. I swear I had found a page that made things about variance click, but it didn't stay in my brain and I can't find it anymore... it definitely was not the nomicon.
The one thing that makes a difference is the RefCell
. Removing it makes it compile. And I don't get what kind of difference RefCell
does as far as the lifetimes are concerned (and I can't do that change because it's really tar
that uses RefCell
.
I feel the root cause of the problem is the Iterator trait, but I can't do anything about that. It seems to me a transmute to force the lifetime would be sound as long as I only touch the Entry while it's alive, but I'm not entirely sure.