Generic lifetime not allowed for associated type Item for Iterator trait

Hi fellow rustacean! I try to implement the Iterator trait for a custom struct Foo, which owns a vector of Rc<RefCell<Bar>> and I want to emit a std::cell::Ref to each of Bar as the Item.

I tried to specify the lifetime of Ref with '_, but the compiler complains that '_ is not allowed here.

use std::cell::{Ref, RefCell};
use std::rc::Rc;

struct Bar{}
struct Foo{
   data: Vec<Rc<RefCell<Bar>>>
}

impl Iterator for Foo{
     // how can i correctly specify Item here ?
     type Item = Ref<'_, Bar>;
     fn next(&mut self) -> Option<Self::Item>{
          todo!()
     }
}

// the lifetime relationship i wanna express is like the following
fn next(&'a mut self) -> Option<Ref<'a, Bar>>

thanks in advance:)

That's classic “something people want but wouldn't get any time soon”.

What you are describing is classic LendingIterator which even sprouted creation of GAT's which were stabilized in rust 1.65.

But, unfortunately, the normal Iterator is not lending and turning it into lending one would be a backward-incompatible change thus no, that wouldn't work.

2 Likes

the LendingIterator is nowhere near, the traditional way is to create a separate type for the iterator, which typically holds a reference to the actual container type, plus some mutable states as the "cursor".

struct FooIter<'b> {
    foo: &'b Foo,
    index: usize,
}
impl<'b> Iterator for FooIter<'b> {
    type Item = Ref<'b, Bar>;
    //...
}
// explicitly get an iterator
impl Foo {
    fn iter(&self) -> FooIter<'_> {
        FooIter {
            foo: self,
            index: 0,
        }
    }
}

in this case, because your container is just a wrapper of Vec, your iterator type can be implemented as a wrapper over the iterator type of Vec, i.e. std::slice::Iter<'_>, something like:

struct FooIter<'b> {
    inner: std::slice::Iter<'b, Rc<RefCell<Bar>>>
}
impl<'b Iterator for FooIter<'b> {
     type Item = Ref<'b, Bar>;
     fn next(&mut self) -> Option<Self::Item>{
          self.inner.next.map(|bar| bar.borrow())
     }
}
impl Foo {
    fn iter(&self) -> FooIter<'_> {
        FooIter {
            inner: self.data.iter()
        }
    }
}

6 Likes

Hi @nerditation, thanks for the response!

My naming is a little bit misleading here. My actual use case is little bit complex than this. I have another more complex data structure say Baz, when about to iterate Baz, I will selectively clone some Rc<RefCell<Bar>> into a Vec. There is actually no struct owns the Vec<Rc<RefCell<Bar>>>. Can i create this Vec on the fly when iterating and store it into BazIter? It's better to name Foo as BazIter.

// create on the fly when iterating Baz
struct Baz{
     fn iter(&self) -> BazIter{
         BazIter {
             data: // selectively clone some Rc
         }
     }
}
struct BazIter{
    data: Vec<Rc<RefCell<Bar>>>,
    cursor: usize
}
impl Iterator for BazIter{
    // how to address this lifetime?
    type Item = Ref<'a, Bar>;
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.