【问题标题】:Using `flat_map` to create iterator over fields for slice of struct使用`flat_map`为结构切片的字段创建迭代器
【发布时间】:2020-09-22 09:37:41
【问题描述】:

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

下面是使用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,使得克隆非常好。 我的直觉告诉我,这应该可行,因为我正在克隆我需要从传入切片中“获取”的唯一东西。显然,我对借用检查器的理解很差,无法掌握这一点。

【问题讨论】:

    标签: rust iterator


    【解决方案1】:

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

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

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

    这将导致在每次迭代中进行堆分配。性能损失可能很小,因为分配器可能能够在每次迭代中重用分配。但是,如果您出于某种原因想避免分配,也可以使用Iterator::chain()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)))
    }
    

    其他替代方案包括implementing IntoIterator and Clone for Game,它允许您简单地使用games.iter().cloned().flatten()iter_vals crate 或使用generators,这是一个不稳定的功能,可以更方便地实现这种迭代器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-30
      • 2021-09-17
      • 2015-02-03
      • 1970-01-01
      • 2011-08-23
      • 2020-10-24
      • 1970-01-01
      • 2019-11-10
      相关资源
      最近更新 更多