How to return an empty iterator if my vector isn't found?

I have a struct like this:

pub struct GameHistory {
    entries: HashMap<TurnData, Vec<HistoryEvent>>,
}

I'd like to write a method that looks up a value in the HashMap and returns an iterator over that vector if it's found, and otherwise returns an empty iterator, i.e. something like this:

pub fn for_turn(&self, turn: TurnData) -> impl Iterator<Item = &HistoryEvent> {
    if let Some(entries) = self.entries.get(&turn) {
        entries.iter()
    } else {
        iter::empty()
    }
}

However, this is not allowed because the two iterators are of different types. Is there an idiomatic way that I can accomplish this? It seems like the sort of thing that should be quite simple, but I can't figure it out.

    pub fn for_turn(&self, turn: TurnData) -> impl Iterator<Item = &HistoryEvent> {
        if let Some(entries) = self.entries.get(&turn) {
            entries.iter()
        } else {
            [].iter()
        }
    }

Edit: to clarify: this works because Vec<T>'s reference iterator comes from &[T]. [] is a constant value, so it can be promoted to a static, which means it's a &'static [T]. That satisfies both the type checker and the borrow checker.

9 Likes

You can also turn the Option into an iterator and flatten it.

pub fn for_turn(&self, turn: TurnData) -> impl Iterator<Item = &HistoryEvent> {
    self.entries
        .get(&turn)
        .into_iter()
        .flat_map(|entries| entries.iter())
}

Edit: flatten works too

pub fn for_turn(&self, turn: TurnData) -> impl Iterator<Item = &HistoryEvent> {
    self.entries.get(&turn).into_iter().flatten()
}
7 Likes

And if you really need two different types, then you can either return Box<dyn Iterator>, or use the either crate to unify their type.

6 Likes