One thing to distinguish is:
-
external iteration
(for x in ... { ... }
syntax, whereby the iterator yields items back to the caller),
This is what the standard Iterator
trait is for.
Workflow
-
Caller: give me an item, please.
-
Iterator: here it is (or not, if exhausted).
-
Caller: Thanks (politeness may compile down to a no-op
)—proceeds to do stuff with that item, such as print!
-ing it or performing an early return
from its own function.
-
Caller: Give me an(other) item, please.
-
Iterator: Here it is (or not etc.),
-
etc.
-
internal iteration
(... .for_each(|x| { ... })
syntax or other adaptors, whereby the iterator is actually an object taking, as a query, a processing request over its internal items
Workflow
-
Caller (thinking out loud): hmm, if I were to have each item of that container, I would like for them to be printed, one at a time
-
Container: I can do that for you, if you so wish.
-
Caller: Really? Great. Yes, please, for each item, I'd like you to do that.
-
Container: Roger roger, will do that. I'll be back once I'm done—proceeds to go to the back room, at which point some metallic clanging noises can be heard.
Now, depending on the Rust data structures, sometimes an item can only be obtained out of a drawer that is about to dangle, and that needs that it be held in place while operating on the item. In that case, it can be extremely painful, or incur in a seemingly unnecessary runtime cost, or even downright impossible to be able to offer external iteration, i.e., to implement Iterator
.
Example [RefCell<Item>]
It is not possible (within sane code), to implement the following API:
fn iter_refcells<'iter, Item> (
refcells: &'iter [RefCell<Item>],
) -> impl 'iter + Iterator<Item = &'iter Item>
It is, however, quite trivial to implement internal iteration on it:
impl<Item> InternallyIterable for [RefCell<Item>] {
type Item = Item;
fn try_fold<Acc, Err, F> (
self: &'_ Self,
mut acc: Acc,
mut f: F,
) -> Result<Acc, Err>
where
F : FnMut(Acc, &'_ Self::Item) -> Result<Acc, Err>,
{
for refcell in self {
acc = f(acc, &*refcell.borrow())?;
}
Ok(acc)
}
}
fn main ()
{
let refcells = [RefCell::new(42), RefCell::new(27)];
let mut sum = 0;
refcells.for_each(|x| {
eprintln!("Got {}", x);
sum += x;
});
dbg!(sum);
}
At this point, we can see that your recursive data-structure can easily implement InternallyIterable
or something similar, like @H2CO3 did.
External iteration can be possible too here, so as long as we avoid creating a recursive type, which is possible by using type-erased iterators, by using virtual method for the dynamic dispatch at runtime:
that is @jethrogb solution.