Help in this very bad implementation of an iterator

    struct TheStruct<'a> {
        buffer: Vec<Option<&'a u8>>,
    }
    trait FakeIterator<'a> {
        fn into_iter(self) -> core::iter::FromFn<impl FnMut() -> Option<&'a mut Option<&'a u8>>>;
    }
    impl<'a> FakeIterator<'a> for &'a mut TheStruct<'a> {
        fn into_iter(self) -> core::iter::FromFn<impl FnMut() -> Option<&'a mut Option<&'a u8>>> {
            core::iter::from_fn(move || {
                self.buffer.push(None);
                let len = self.buffer.len() - 1;
                (*self).buffer.get_mut(len)
            })
        }
     }

I need help as such first I tried Iterator realizing 2 &mut alias
Then IntoIterator but realizing TAITs is needed but it's not stabilized
then this fakeIterator returning an iterator but the error keep saying "reference may escape in FnMut" (paraphased)
I wanted an iterator that never returns None (not iter::cycle as such clones to keep it alive) that mutates TheStruct

Without even going into borrow checking: you can't push into a Vec<_> while keeping references to the Vec<_> items alive, because pushing can reallocate and move the items (which would make the references dangle).


Getting into lifetimes:

    impl<'a> FakeIterator<'a> for &'a mut TheStruct<'a> {

You never want &'a mut Thing<'a> either. That's something useful to know generally. But I won't go into adding another lifetime parameter here, because there are other problems it doesn't help with.

        core::iter::from_fn(move || {
            self.buffer.push(None);
            let len = self.buffer.len() - 1;
            (*self).buffer.get_mut(len)
        })

Even without the push, the compiler can't prove that you never return the same item twice (which would result in multiple active &mut Option<_> to the same place, which is UB). And even if it could, it wouldn't be able to allow it without unsafe, because there are other safe ways to obtain &mut _s to all the items in the Vec<_>. Or to, say, call push.

Even then it probably wouldn't work due to the error about closures you're getting. Back to that in a second.


If you want a &mut _ iterator[1] with safe Rust, you need some way to guarantee the &mut _ items you pass out do not alias each other, and also do not alias the remaining data you hold onto. That's possible if you hold onto a slice, but not a Vec<_>. When your iterator holds a single &mut _ and you want to hand out multiple &mut _s, you need some way to "split the borrow", like I discuss in that walkthrough.

If you don't split the borrow, then you're trying to return a reborrow through your single &mut _. That's not possible with the Iterator trait -- it would be a lending iterator, like was discussed in another thread.

The FnMut(..) trait, which is what would let you call the closure in your code, has a similar signature to Iterator.

// Note: Simplified to make the explanation simpler.
trait FnMut<Args> {
    type Return;
    fn call_mut(&mut self, args: Args) -> Self::Return;
}

There is no place for the lifetime from &mut self to go in the return type, so the return value can't be some reborrow that requires keeping *self borrowed. If the trait supported that pattern, we could say it would be a lending closure. But like Iterator doesn't support lending iterators, FnMut doesn't support lending closures.

That's what the error you are getting about the closure is trying to say.

If you forego the closure and have a named iterator struct and try to implement Iterator directly (with the analogous function body for next), you'll get errors which are trying to say "this trait doesn't support lending iterators".


There might be some solution using a lending iterator trait, or using some arena append-only Vec-like data structure that works with shared references or something. But being able to modify a std::vec::Vec<_> while handing out items that reference the elements is a dead-end for std::iter::Iterator.


  1. as in the std::iter::Iterator â†Šī¸Ž

2 Likes

It is hard to know what you are wanting. Maybe typed-arena* instead of (or as well as) the Vec could get to what you want.

*there is probably something newer, but this name stuck in my head.

2 Likes