Iterator cannot move out of borrowed content

#1

hello everyone I have this code:

impl FetchJob
{
    pub fn fetch_chunk<I>(chunk: usize, iter: Arc<Mutex<I>>) -> impl Future<Item = bool, Error = ()>
    where
        I: Iterator<Item = hyper::Uri>
    {
        //let client = build_https_client().unwrap(); //TODO make this at initialization to not remake the client at every interval.
        let client = Client::new();
        let mut requests: Vec<_> = Vec::new();
        for (uri, count) in &*iter.lock().expect("error") {
            requests.push(client
                .clone()
                .get(uri)
                .map(|res| {
                    println!("Response: {}", res.status());
                    println!("Headers: {:#?}", res.headers());
                })
                .map_err(|e| println!("{:#?}", e)));
        }
        future::join_all(requests)
            .map(move |res: Vec<()>| res.len() == chunk)
            .map_err(|_| ())
    }
}

struct FetchStream<I> {
    uri_iter: Arc<Mutex<I>>,
    interval: Interval,
    time_pass: TimePass,
    fetch_job: FetchJob,
    fetch_future: Option<Box<dyn Future<Item = bool, Error = ()> + Send>>,
}

impl<I> FetchStream<I> {
    pub fn new(chunk: usize, interval: std::time::Duration, iter: I ) -> Self {
        Self {
            uri_iter: Arc::new(Mutex::new(iter)),
            interval: Interval::new_interval(interval),
            time_pass: TimePass::Ready,
            fetch_job: FetchJob {
                chunk
            },
            fetch_future: None
        }
    }
}

impl<I> Future for FetchStream<I>
where
    I: Iterator<Item = hyper::Uri> + 'static,
{
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, ()> {
        loop {
            match self.time_pass {
                TimePass::Ready => {
                    if let Some(_) = self.fetch_future {
                        let res: bool = try_ready!(self.fetch_future.poll().map_err(|_| ())).unwrap();
                        if !res {
                            return Ok(Async::Ready(()))
                        }
                    } else {
                        let iter = self.uri_iter.clone();
                        self.fetch_future: Option<Box<dyn Future<Item = bool, Error = ()> + Send>> =
                            Some(Box::new(FetchJob::fetch_chunk(23, iter)));

but it gives me:

error[E0277]: `&I` is not an iterator
  --> src/fetch/fetch_stream.rs:86:29
   |
86 |         for (uri, count) in &*iter.lock().expect("error") {
   |                             -^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                             |
   |                             `&I` is not an iterator
   |                             help: consider removing 1 leading `&`-references
   |
   = help: the trait `std::iter::Iterator` is not implemented for `&I`
   = note: required by `std::iter::IntoIterator::into_iter`

but even if I take off the & I get:

error[E0507]: cannot move out of borrowed content
  --> src/fetch/fetch_stream.rs:85:20
   |
85 |         for uri in *iter.lock().expect("error") {
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content

I have a Arc<mutex<>> around it so I don’t see why it is complaining about it.

0 Likes

#2

For loop needs an owned iterator and locking a mutex will give you only a temporary &mut borrow. Fortunately, there’s a by_ref wrapper that allows you to treat a reference to iterator also as iterator:

for uri in iter.lock().expect("error").by_ref() { … }

Please note that after such a loop the iterator would be drained and any second attempt to iterate will return no items.

Sidenote: If by_ref wrapper did not exist, you could also iterate “manually”:

let mut locked_iter = iter.lock().expect("error");
while let Some(item) = locked_iter.next() { … }
0 Likes

#3

The thing is that it will be called into this function multiple times with the same iterator

0 Likes

#4

Is there a way to go around this?

0 Likes

#5

Oh, let me try the manual way

0 Likes

#6

You can require Clone on your iterator, and clone the actual iterator (without the Mutex).

Perhaps you don’t want an Iterator bound on I but IntoIterator?

Note that the while let loop will have the same draining behaviour as by_ref.

0 Likes

#7

I saw the IntoIterator implementation for Vec but it just calls some self.iter() method which I did not find a def for. I do not know how to implement IntoIter

0 Likes

#8

Do you have a sample implementation for IntoIterator? This is what I have currently:

impl Iterator for TestSource {
    type Item = Uri;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count == 3 {
            None
        } else {
            Some(self.url.clone().parse::<hyper::Uri>().unwrap())
        }
    }
}

how would I convert this into a IntoIterator that will work in a for loop with as a reference?

0 Likes

#9

well the while will also drain it unless you call break.

0 Likes

#10

You may want to split TestSource to TestSource and TestSourceIter:

struct TestSourceIter {
    // all the fields from your TestSource
}

impl Iterator for TestSourceIter {
    type Item = Uri;

    fn next(&mut self) -> Option<Self::Item> { /* same as before */ }
}

struct TestSource {
    // fields that are needed to create TestSourceIter (so probably url, but not count)
}

impl IntoIterator for TestSource {
    type Item = Uri;
    type IntoIter: TestSourceIter;
    fn into_iter(self) -> TestSourceIter { … }
}

This solution will also require cloning just before iterator. You can also impl IntoIterator for &TestSource instead, but I think it’ll be harder to write a bound for that:

impl<'a> IntoIterator for &'a TestSource {
    type Item = Uri;
    type IntoIter: TestSourceIter;
    fn into_iter(self) -> TestSourceIter { … }
}
0 Likes

#11

ok thanks I will try that out.

0 Likes