I am trying to adapt a generic associated type with a lifetime parameter (Item in LendingIterator in the example) to an associated type that does not a have lifetime parameter. I would expect this to work if the lifetime of the GAT is 'static. Is there a way to restrict the GAT to make this example compile?
pub trait LendingIterator {
type Item<'a> where Self: 'a;
fn next_borrowed(&mut self) -> Option<Self::Item<'_>>;
}
struct Adapter<I>(I);
impl<T: LendingIterator + 'static> Iterator for Adapter<T> where T::Item<'static>: 'static {
type Item = T::Item<'static>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next_borrowed()
}
}
Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
--> src/lib.rs:11:9
|
10 | fn next(&mut self) -> Option<Self::Item> {
| - let's call the lifetime of this reference `'1`
11 | self.0.next_borrowed()
| ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
error: could not compile `playground` due to previous error
I think you need to be able to say that for<'a> T::Item<'a>: 'static, but I haven't been able to find a way to make that work either after playing around with it a bit
pub trait LendingIterator {
type Item<'a>
where
Self: 'a;
fn next_borrowed(&mut self) -> Option<Self::Item<'_>>;
}
struct Adapter<I>(I);
impl<Item, T: 'static + for<'a> LendingIterator<Item<'a> = Item>> Iterator for Adapter<T>
where
T::Item<'static>: 'static,
{
type Item = Item;
fn next(&mut self) -> Option<Self::Item> {
self.0.next_borrowed()
}
}
struct Demonstration<T>(std::vec::IntoIter<T>);
impl<T> LendingIterator for Demonstration<T> {
type Item<'a> = T
where Self: 'a;
fn next_borrowed(&mut self) -> Option<Self::Item<'_>> {
self.0.next()
}
}
fn demonstration() {
let x = 1;
let y = 2;
let z = 3;
let d = Adapter(Demonstration(vec![1, 2, 3].into_iter()));
for v in d {
println!("{v}");
}
}
But real lifetime GATs are still kind-of weak… the “fake” GATs that have been stable for a while work better, and allow to avoid the T: static bound entirely; on the other hand, every way I tried so far with the real GATs ran into errors claiming “due to current limitations in the borrow checker, this implies a `'static` lifetime”.
pub trait GatLessLendingIterator: for<'a> HasGatLessLendingIteratorItem<'a> {
fn next_borrowed(&mut self) -> Option<GatLessLendingIteratorItem<'_, Self>>;
}
pub trait HasGatLessLendingIteratorItem<'a, _Bound = &'a Self> {
type Item;
}
pub type GatLessLendingIteratorItem<'a, Self_> = <Self_ as HasGatLessLendingIteratorItem<'a>>::Item;
struct Adapter<I>(I);
impl<Item, T: GatLessLendingIterator> Iterator for Adapter<T>
where
T: for<'a> HasGatLessLendingIteratorItem<'a, Item = Item>,
{
type Item = Item;
fn next(&mut self) -> Option<Self::Item> {
self.0.next_borrowed()
}
}
struct Demonstration<T>(std::vec::IntoIter<T>);
impl<T> GatLessLendingIterator for Demonstration<T> {
fn next_borrowed(&mut self) -> Option<GatLessLendingIteratorItem<'_, Self>> {
self.0.next()
}
}
impl<'a, T> HasGatLessLendingIteratorItem<'a> for Demonstration<T> {
type Item = T;
}
fn demo1<T>(x: Adapter<Demonstration<T>>) {
for _ in x {}
}
fn demo2() {
let x = 1;
let y = 2;
let z = 3;
let d = Adapter(Demonstration(vec![&x, &y, &z].into_iter()));
for v in d {
println!("{v}");
}
}
The downside of course is that the impls of such “fake GAT” traits is more verbose.
Thanks as well for the article. I am afraid that is too complicated for my use case.
Luckily I do not need mutable access the object that has the GAT and hence (for reasons unclear to me) I am able to access the non-'static data through a pointer. My implementation now looks closer to this:
This unfortunately does not allow for owning iterators.