Return an iterator from struct in RefCell

I have a struct that represents a container and has a Vec internally. This struct is always available inside RefCell. I need to return an iterator over this vector.

My example does not compile with the message:

hidden type for `impl Iterator<Item = Rc<Foo>>` captures lifetime that does not appear in bounds.

I understand why this happens: I have a Ref<Vec<_>> returned from self.borrow() and an iterator returned by iter() holds the reference to Vec<_>, which lives only while Ref<Vec<_>> lives, which will be dropped at the exit from the function.

But can I still achieve what I want somehow?

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

struct Foo(RefCell<FooInner>);
struct FooInner(i32);

struct Container(RefCell<ContainerInner>);
type ContainerInner = Vec<Weak<Foo>>;

impl Container {
    fn new() -> Rc<Container> {
        Rc::new(Container(RefCell::new(Vec::new())))
    }

    fn push(self: &Self, foo: &Rc<Foo>) {
        self.0.borrow_mut().push(Rc::downgrade(foo));
    }

    fn iter(self: &Self) -> impl Iterator<Item = Rc<Foo>> {
        self.0.borrow().iter().filter_map(|it| it.upgrade())
    }
}

fn main() {
	let cont = Container::new();
	let foo = Rc::new(Foo(RefCell::new(FooInner(100))));
	cont.push(&foo);
}

Generally, working with things behind RefCell can be quite inconvenient, and this is one of the ways that happens. The only way I know of doing this is to build your own iterator:

fn iter(&self) -> impl Iterator<Item = Rc<Foo>> + '_ {
    struct MyIter<'a> {
        rc: &'a Container,
        pos: usize,
    }
    
    impl<'a> Iterator for MyIter<'a> {
        type Item = Rc<Foo>;
        
        fn next(&mut self) -> Option<Rc<Foo>> {
            loop {
                let pos = self.pos;
                self.pos += 1;
                match self.rc.0.borrow().get(pos).map(|it| it.upgrade()) {
                    Some(Some(upgraded)) => return Some(upgraded),
                    Some(None) => { /* upgrade failed - go around loop */ }
                    None => return None
                }
            }
        }
        
        fn size_hint(&self) -> (usize, Option<usize>) {
            let len = self.rc.0.borrow().len();
            let size = len.saturating_sub(self.pos);
            (size, Some(size))
        }
    }

    MyIter {
        rc: self,
        pos: 0,
    }
}

playground

1 Like

That's great. I also came up with my version of my own iterator (Yours is much more elegant and I learned something from reading your code, thank you).

But I can easily imagine having other data structure that is not that easy to create an iterator for. Or even impossible due to visibility rules. I've tried to make some wrapper iterator, that will hold both an iterator and Ref<Container> so that Ref does not go out of scope and they share one lifetime. But looks like this is impossible.

This does not compile, because of borrowing wrap.c when calling wrap.c.iter()

    fn iter(self: &Self) -> impl Iterator<Item = Rc<Foo>> + '_ {
		let mut wrap = IteratorWrap { c: self.0.borrow(), i: None };
		wrap.i = Some(wrap.c.iter().filter_map(|it| it.upgrade()));
		wrap
    }
...
struct IteratorWrap<'a, C, I> {
    c: Ref<'a, C>,
    i: Option<I>,
}

impl<'a, C, I, T> Iterator for IteratorWrap<'a, C, I>
where
    I: Iterator<Item = Rc<T>> + 'a,
{
    type Item = Rc<T>;

    fn next(&mut self) -> Option<Self::Item> {
        self.i.unwrap().next()
    }
}

Your IteratorWrap type is a self-referential type, and those cannot be written in safe Rust. (Self-referential means that it holds a reference that points into another field of itself.)

If you wanted to make the iterator outlast the borrow, you'd have to collect its results into a Vec or another structure. But there's a way to do it with Ref::map() if you're fine with keeping the borrow around until the iterator is dropped:

fn borrowed_iter(&self) -> impl Iterator<Item = Rc<Foo>> + '_ {
    let mut items = Some(Ref::map(self.0.borrow(), |vec| &vec[..]));
    Box::new(std::iter::from_fn(move || {
        let mut item = None;
        let rest = Ref::map(items.take()?, |items| {
            let mut iter = items.iter();
            item = iter.find_map(Weak::upgrade);
            iter.as_slice()
        });
        if item.is_some() {
            items = Some(rest);
        }
        item
    }))
}

This has the advantage that the items can't change out from under you while you're in the middle of iterating through them.

Alternatively, @alice's iterator can be written as a map_while over the indices:

fn indexed_iter(&self) -> impl Iterator<Item = Rc<Foo>> + '_ {
    (0..)
        .map_while(|i| self.0.borrow().get(i).map(Weak::upgrade))
        .flatten()
}

And if you ever need an iterator to the items by reference (instead of creating new owned values), you can use Ref::map_split():

fn projected_iter(&self) -> impl Iterator<Item = Ref<'_, Weak<Foo>>> + '_ {
    let mut items = Some(Ref::map(self.0.borrow(), |vec| &vec[..]));
    std::iter::from_fn(move || {
        if items.as_ref().unwrap().is_empty() {
            return None;
        }
        let (first, rest) =
            Ref::map_split(items.take().unwrap(), |items| items.split_first().unwrap());
        items = Some(rest);
        Some(first)
    })
}

Except using crates such as ouroboros to help out. (Though in this example – on stable Rust – this still involves a bit of an effort to cope with the unnamable mapped closure type.)

/*
[dependencies]
ouroboros = "0.15.5"
*/

use ouroboros::self_referencing;

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

#[derive(Debug)]
struct Foo(RefCell<FooInner>);
#[derive(Debug)]
struct FooInner(i32);

struct Container(RefCell<ContainerInner>);
type ContainerInner = Vec<Weak<Foo>>;

impl Container {
    fn new() -> Rc<Container> {
        Rc::new(Container(RefCell::new(Vec::new())))
    }

    fn push(self: &Self, foo: &Rc<Foo>) {
        self.0.borrow_mut().push(Rc::downgrade(foo));
    }

    fn iter(self: &Self) -> impl Iterator<Item = Rc<Foo>> + '_ {
        // used below, helps type inference keep the self-referential lifetime
        // out of the unnamable closure type by prescribing a more generic signature
        fn generic_closure<F: FnMut(&Weak<Foo>) -> Option<Rc<Foo>>>(f: F) -> F {
            f
        }
        IteratorWrapBuilder {
            c: self.0.borrow(),
            i_builder: |c| c.iter().filter_map(generic_closure(|it| it.upgrade())),
        }
        .build()
    }
}

fn main() {
    let cont = Container::new();
    let foo = Rc::new(Foo(RefCell::new(FooInner(100))));
    cont.push(&foo);
    for c in cont.iter() {
        dbg!(c);
    }
}

#[self_referencing]
struct IteratorWrap<'a, F> {
    c: Ref<'a, ContainerInner>,
    #[borrows(c)]
    #[covariant]
    i: std::iter::FilterMap<std::slice::Iter<'this, Weak<Foo>>, F>,
}

impl<'a, F> Iterator for IteratorWrap<'a, F>
where
    F: FnMut(&Weak<Foo>) -> Option<Rc<Foo>>,
{
    type Item = Rc<Foo>;

    fn next(&mut self) -> Option<Self::Item> {
        self.with_i_mut(|i| i.next())
    }
}

(run online)

Thank you all for all your input.

After a close examination of what I'm doing I found myself in a situation where an inner container is not actually a Vec, but a BTreeMap.

I understand that steffahn's should work, but I'm still trying to figure out the more clear solution.

Basically, I need to return something that will hold Ref while iterating. For now, I came up with this:

struct ContainerIterable<'a> {
    container: Ref<'a, ContainerInner>,
}

impl<'a> ContainerIterable<'a> {
    fn iter(&self) -> impl Iterator<Item = Rc<Foo>> + '_ {
        self.container.iter().filter_map(|weak| weak.upgrade())
    }
}
...
    fn iter<'a>(self: &'a Self) -> ContainerIterable<'a> {
        ContainerIterable {
            container: self.0.borrow(),
        }
    }
...
    for foo in cont.iter().iter() {
        dbg!(foo.0.borrow().0);
    }

That's ugly because of this extra structure, which is not actually needed, and because of iter().iter() instead of just iter().

The best solution I see now is to expose the inner type in the public interface and to allow borrowing it from RefCell, this will solve all the problems. But that complicates the external interface.

For now, it feels like I have to avoid RefCells as much as possible, and what I'm trying to do is just not a rust way.

You could consider – given that the iter method, too, will expose some kind of “locking” behavior anyways where while the iterator is alive, mutating access to the container will result in panicking from the RefCell – just expose an explicit “locking” method, similar to RefCell’s .borrow() method, on your Container struct. This method – let’s call it fn read_lock(&'a self) for now – will return some kind of ContainerReadLock<'a> struct which just wraps the Ref<…> value. That struct could then re-expose the immutable API from Container, but saving the (small) overhead of doing a new RefCell::borrow call for each call, and more notably only that type would then have a .iter() method (and a corresponding IntoIterator for &ContainerReadLock<'_> impl).

1 Like

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.