How to return an impl Iterator from inside a RefCell?

#1

I recently discovered std::cell::Ref::map, which is a wonderful function for creating accessors for data inside a RefCell. I am now wondering if there is any way to get an impl Iterator out of a RefCell in some way. I’m running into trouble due to the fact that although Ref implements Deref into an Iterator type, that doesn’t make the Ref itself an Iterator. Here is a playground version of my attempt at this.

Any idea how I could achieve this? Obviously, I could return a Ref<ExplicitIteratorType>, but that leaks my implementation (and has a very long type), or requires me to create a wrapper struct, which is also a nuisance.

0 Likes

#2

A Ref<impl Iterator<Item = ...>> is useless, because all iterators need to be mutable to run Iterator::next, so I will assume you will change that to RefMut<impl Iterator<Item = ...>>. Now, you could just get a &mut out of the RefMut, and a mutable reference to an iterator is also an iterator. This can be done as shown below.

this impl Iterator is not valid, I am just using it as a palceholder for your RefMut

let ref_mut : std::cell::RefMut<'_, impl Iterator> = ...;
let iter : &mut _ = &mut *ref_mut;
0 Likes

#3

Yeah, I see there are several problems with my solution (and with my “obviousl I could” alternative. Right now I can see no way to create an Iterator to an object in a RefCell short of creating a custom struct to hold a Ref.

Is there any way to return an iterator to an object held in a RefCell?

0 Likes

#4

Unfortunately you can’t extract it to a new function, the closest you can get is this.

You can inline the function and that will work playground link

0 Likes

#5

It looks like that doesn’t work either. :frowning:

Alas, I had hoped to abstract away the iteration process so that I could more easily change implementation later. :frowning:

0 Likes

#6

Is your RefCell wrapped in a Rc? Or can you access a mutable reference to your RefCell? If so, you can use RefCell::get_mut

0 Likes

#7

That wouldn’t help as I’m trying to create a general accessor which shouldn’t require mut access in order to read the contents. :frowning:

0 Likes

#8

Can you use callbacks?

0 Likes

#9

I could use callbacks, but that would defeat the goal of providing a standard and familiar interface. Worst case scenario would be a wrapper struct that holds a Ref and a usize Iterator. It’s a little inefficient to do bounds checks, but would beat a callback approach in both efficiency and ease of use.

It’s disappointing that we don’t seem to have a zero cost abstraction for this use case.

0 Likes

#10

Ok, so something like this?

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0ed38eeb25fe9fe023196021949ddf0a

use std::cell::RefMut;

pub struct RefMutIter<'a, I>(pub RefMut<'a, I>);

impl<I: Iterator> Iterator for RefMutIter<'_, I> {
    type Item = I::Item;
    
    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }
}
0 Likes

#11

Like that, but there isn’t a way to construct your type from something that you want to iterate over like a slice or a Vec.

0 Likes

#12

Can you tolerate a bit of unsafe code?

0 Likes

#13

Actually, I’ve managed to remove the need for interior mutability, so this problem no longer is impacting me.

0 Likes

#14

Ok, cool.

I just found a maybe workable solution. So if it comes up later you could use it! (No unsafe code)

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=feb0fdb049562431bf9745205eb26a25

0 Likes

#15

I don’t see here where you enable getting an iterator for a Vec in a RefMut. Can you add code to demonstrate how to use it?

0 Likes

#16

Actually, now that I look at this again, this isn’t much better than just returning a RefMut, so I don’t have a good solution short of using unsafe code.

0 Likes