Borrowing from data the iterator owns while iterating

I would like to be able to iterate over a series of owned items whole also getting a reference to an item owned by the iterator.

Like this (code doesn't compile because the lifetimes are missing, but it shows what I'd like to achieve):

use std::collections::HashMap;

struct Things {
    things: Vec<String>,
    singleton: HashMap<String, String>,
}

struct ThingsIntoIterator {
    things: std::vec::IntoIter<String>,
    singleton: HashMap<String, String>,
}

impl IntoIterator for Things {
    type Item = (String, &HashMap<String, String>); // lifetimes?
    type IntoIter = ThingsIntoIterator;

    fn into_iter(self) -> Self::IntoIter {
        ThingsIntoIterator {
            things: self.things.into_iter(),
            singleton: self.singleton
        }
    }
}

impl Iterator for ThingsIntoIterator {
    type Item = (String, &HashMap<String, String>); // here as well

    fn next(&mut self) -> Option<Self::Item> {
        self.things.next().map(|thing| (thing, &self.singleton))
    }
}

Where the iterator consumer gets each String from things owned and a reference to singleton for as long as the iterator is alive. I don't want to just iterate over things and reference singleton in map and co closures because I would like to transfer ownership of ThingsIntoIterator to someone else consuming it. I think that this should be possible, I just don't know how to express the lifetimes of the GATs to tell Rust that the hashmap reference points to ThingsIntoIterator.

1 Like

This is something the Iterator trait cannot provide. Because the Item doesn't have a lifetime parameter and so it cannot reference data borrowed from &mut self.

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>
}

You will need to implement a lending iterator pattern:

pub trait LendingIterator {
    type Item<'a>
    where
        Self: 'a;

    fn next(&mut self) -> Option<Self::Item<'_>>;
}
5 Likes

Note that the above example shows how a minimal lending iterator trait could work, but in practice, GATs are too restrictive (expressing bounds on the associated Item<'_> is difficult). I like the approach employed by the lender crate[1].


  1. lender is unsound for other reasons, so even though its polyfill for better GATs is great, I wouldn’t necessarily recommend using lender. ↩︎

2 Likes