3

Here is as far as I could get, using rental, partly based on How can I store a Chars iterator in the same struct as the String it is iterating on?. The difference here is that the get_iter method of the locked member has to take a mutable self reference.

I'm not tied to using rental: I'd be just as happy with a solution using reffers or owning_ref.

The PhantomData is present here just so that MyIter bears the normal lifetime relationship to MyIterable, the thing being iterated over.

I also tried changing #[rental] to #[rental(deref_mut_suffix)] and changing the return type of MyIterable.get_iter to Box<Iterator<Item=i32> + 'a> but that gave me other lifetime errors originating in the macro that I was unable to decipher.

#[macro_use]
extern crate rental;

use std::marker::PhantomData;

pub struct MyIterable {}

impl MyIterable {
    // In the real use-case I can't remove the 'mut'.
    pub fn get_iter<'a>(&'a mut self) -> MyIter<'a> {
        MyIter {
            marker: PhantomData,
        }
    }
}

pub struct MyIter<'a> {
    marker: PhantomData<&'a MyIterable>,
}

impl<'a> Iterator for MyIter<'a> {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        Some(42)
    }
}

use std::sync::Mutex;

rental! {
    mod locking_iter {
        pub use super::{MyIterable, MyIter};
        use std::sync::MutexGuard;

        #[rental]
        pub struct LockingIter<'a> {
            guard: MutexGuard<'a, MyIterable>,
            iter: MyIter<'guard>,
        }
    }
}

use locking_iter::LockingIter;

impl<'a> Iterator for LockingIter<'a> {
    type Item = i32;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.rent_mut(|iter| iter.next())
    }
}

struct Access {
    shared: Mutex<MyIterable>,
}

impl Access {
    pub fn get_iter<'a>(&'a self) -> Box<Iterator<Item = i32> + 'a> {
        Box::new(LockingIter::new(self.shared.lock().unwrap(), |mi| {
            mi.get_iter()
        }))
    }
}

fn main() {
    let access = Access {
        shared: Mutex::new(MyIterable {}),
    };
    let iter = access.get_iter();
    let contents: Vec<i32> = iter.take(2).collect();
    println!("contents: {:?}", contents);
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91
Oliver Goodman
  • 671
  • 6
  • 14
  • 1
    If the only problem is the `mut`, just using `#[rental_mut]` instead of `#[rental]` should fix it, shouldn't it? – rodrigo Aug 03 '18 at 14:27
  • oboy, how did i not spot that? thank you very much! – Oliver Goodman Aug 03 '18 at 21:20
  • 2
    If that is the solution, you should write an answer and accept it, instead of editing it into the question. It's normal to answer your own question and makes it easier for future readers to know the question is solved. – trent Aug 03 '18 at 21:26
  • @trentol, done. It seems i have to wait 24h before i'm allowed to accept it :). – Oliver Goodman Aug 03 '18 at 21:36

1 Answers1

4

As user rodrigo has pointed out in a comment, the solution is simply to change #[rental] to #[rental_mut].

Oliver Goodman
  • 671
  • 6
  • 14