How return this Box'd Iterator, borrowing from RefCell?

Playground here. I had a variation of this code working, but now I’ve added some interior mutability to the Thing, so my implementation of iter_children for Thing is no longer good enough. The goal is to return from iter_children an abstract iterator over Rc<dyn Node> items.

Is there a way to adjust this code so it works with the borrowed state? Maybe with complicated iterators like this it would be easier to use an “internal” iterator – ie, pass a dyn Fn(&dyn Node) → () to the iter_children function, as opposed to trying to return in iterator?

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

type ChildIter<'a> = Box<dyn DoubleEndedIterator<Item = Rc<dyn Node + 'static>> + 'a>;

trait Node { 
    fn iter_children(&self) -> Option<ChildIter>;
}

struct Thing {
    state: RefCell<State>
}
struct ChildInfo {
    child: Rc<dyn Node>,
    other: u32,
}
struct State {
    nodes: Vec<ChildInfo>
}

impl Node for Thing {
    fn iter_children(&self) -> Option<ChildIter> {
        let b: Ref<State> = self.state.borrow();
        Some( Box::new( b.nodes.iter().map(|info| info.child.clone())) )
    }
}

The error:

error[E0515]: cannot return value referencing local variable `b`
  --> src/main.rs:26:9
   |
26 |         Some( Box::new( b.nodes.iter().map(|info| info.child.clone())) )
   |         ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |               |
   |         |               `b` is borrowed here
   |         returns a value referencing data owned by the current function

For this case I'd suggest making your own iter using Ref<'_, [ChildInfo]>.[1]

The Ref<'_, _> is the lock-like thing granting you access to the contents. Once you drop it or let it go out of scope, you lose that access. So if you want to keep that access alive in the returned iterator, the iterator has to contain the Ref.

However, you have to borrow the Ref itself to get a &_ reference to the actual contents. Trying to return both the Ref and a &_ to the contents at the same time is therefore an attempt to return a self-referential struct. There may be crates that can do this using unsafe, but it's generally a very tricky thing to get correct (not be undefined behavior).

By creating your own iterator, you can stick to creating &_ through the Ref just within next/next_back.


  1. untested/should have a constructor/etc ↩︎

2 Likes

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.