Midnight Exigent
Midnight Exigent

Reputation: 625

Return a reference to a member iterator

I have a struct that has an iterator over a borrowed slice, and I want to create a method that returns an iterator that does some computation on the contained iterator

Basically this:

#[derive(Clone)]
struct Foo<'a>  {
    inner: ::std::iter::Cycle<::std::slice::Iter<'a, u8>>,
}


impl<'a> Foo<'a> {
    pub fn new<T: AsRef<[u8]> + ?Sized>(vals: &'a T) -> Foo<'a> {
        Self {
            inner: vals.as_ref().iter().cycle(),
        }
    }
    pub fn iter(&mut self) -> impl Iterator<Item = u8> + 'a {
        self.inner.by_ref().map(Clone::clone) // simple case but this could be any computation
    }
}

Which yields a cryptic error.

Here's my understanding: I need to bind the returned iterator's lifetime to the &mut self of iter() so that &mut self is only dropped when .collect() is called on the returned iterator.

BUT, changing to fn iter(&'a mut self) isn't good enough because &'a mut self now lives for the entire duration of the struct instance (meaning that calling collect() doesn't drop that reference). Which prevents something like this

#[test]
fn test(){
    let mut foo = Foo::new("hello, there");
    foo.iter().zip(0..4).collect::<Vec<_>>(); // call once, ok
    foo.iter().zip(0..4).collect::<Vec<_>>(); // error because 2 &mut references exist at the same
}

rust playground link

Upvotes: 1

Views: 102

Answers (1)

Hadus
Hadus

Reputation: 1674

The way I would do this is to make a struct just for iterating over those items. It makes things much easier.

    ...

    pub fn iter(&mut self) -> ClonedFooIter<'a, '_> {
        ClonedFooIter {
            inner: &mut self.inner,
        }
    }
}

pub struct ClonedFooIter<'a, 'b> {
    inner: &'b mut std::iter::Cycle<::std::slice::Iter<'a, u8>>,
}

impl<'a, 'b> Iterator for ClonedFooIter<'a, 'b> {
    type Item = u8;

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

The idea is that the returned struct only lives till the mutable borrow of self lives.

Using a struct makes naming the iterator type easier too.

Upvotes: 1

Related Questions