首页 > 解决方案 > 使用`flat_map`为结构切片的字段创建迭代器

问题描述

给定结构的集合(向量/切片)。如何在每个结构中的某些字段上创建组合迭代器?

以下是使用的具体尝试flat_map

struct Game {
    home_team: u8,
    away_team: u8,
}

fn teams(games: &[Game]) -> impl Iterator<Item = u8> {
    games
        .iter()
        .flat_map(|game| [game.home_team, game.away_team].iter().map(|x| x.clone()))
}
fn main() {
    let data = &[
        Game {
            home_team: 1,
            away_team: 2,
        },
        Game {
            home_team: 1,
            away_team: 3,
        },
    ];
    let non_unique_teams: Vec<u8> = teams(data).collect();
}

我的实际用例非常相似。特别是,构成迭代器基础的字段实现了Copy,使克隆非常好。我的直觉告诉我这应该可行,因为我正在克隆我需要从传入切片中“获取”的唯一东西。显然,我对借用检查器的理解对我来说太差了。

标签: rustiterator

解决方案


迭代器需要拥有包含结构字段副本的内存。在您的代码中,您创建一个本地数组并对其进行调用iter(),这会导致对不拥有数据的切片引用进行迭代。

让迭代器拥有数据的最简单方法是为每个结构分配一个向量:

fn teams(games: &[Game]) -> impl Iterator<Item = u8> + '_ {
    games
        .iter()
        .flat_map(|game| vec![game.home_team, game.away_team])
}

这将导致每次迭代中的堆分配。性能损失可能很小,因为分配器可能能够在每次迭代中重用分配。但是,如果您出于某种原因想要避免分配,您也可以使用Iterator::chain()and的组合std::iter::once()

use std::iter::once;

fn teams(games: &[Game]) -> impl Iterator<Item = u8> + '_ {
    games
        .iter()
        .flat_map(|game| once(game.home_team).chain(once(game.away_team)))
}

其他替代方案包括实现IntoIteratorCloneforGame,这将允许您简单地使用crate或使用generatorsgames.iter().cloned().flatten(),这是一个不稳定的功能,使实现这种迭代器更加方便。iter_vals


推荐阅读