首页 > 解决方案 > Rust 中枚举的展平向量

问题描述

我正在尝试Enum在 Rust 中展平一个向量,但我遇到了一些问题:

enum Foo {
    A(i32),
    B(i32, i32),
}

fn main() {
    let vf = vec![Foo::A(1), Foo::A(2), Foo::B(3, 4)];

    let vi: Vec<i32> = vf
        .iter()
        .map(|f| match f {
            Foo::A(i) => [i].into_iter(),
            Foo::B(i, j) => [i, j].into_iter(),
        })
        .collect(); // this does not compile

    // I want vi = [1, 2, 3, 4]. vf must still be valid
}

我可以只使用常规的 for 循环并将元素插入现有的向量中,但这并不有趣。我想知道是否有更惯用的 Rust 方式来做到这一点。

标签: rust

解决方案


这是一种产生迭代器的方法(而不是像fold()基于解决方案那样的向量)。

use std::iter::once;

enum Foo {
    A(i32),
    B(i32, i32),
}

fn main() {
    let vf = vec![Foo::A(1), Foo::A(2), Foo::B(3, 4)];

    let vi: Vec<i32> = vf
        .iter()
        .flat_map(|f| {
            match f {
                &Foo::A(i) => once(i).chain(None),
                &Foo::B(i, j) => once(i).chain(Some(j)),
            }
        })
        .collect();

    dbg!(vi);
}

这基本上与您尝试做的事情相同,但以一种会成功的方式。以下是我更改的部分,按照它们在代码中出现的顺序:

  1. 我用.flat_map()而不是.map(). flat_map接受一个函数,该函数返回一个迭代器并生成该迭代器的元素(“展平”),而.map()刚刚给出了迭代器。

  2. &match图案中使用过。这是因为,由于您.iter()在向量上使用(这适合您的要求“<code>vf 必须仍然有效”),您有对枚举的引用,并且对枚举的引用的模式匹配通常会为您提供引用到它的元素,但我们几乎可以肯定想要i32按值处理 s 。我还可以做其他几件事,例如*在值上使用取消引用运算符,但这是简洁而整洁的。

  3. 你尝试了.into_iter()一个数组。不幸的是,在当前的 Rust 中,这并不能满足您的要求,并且您实际上无法返回该迭代器,原因有些尴尬(这将在即将到来的 Rust 版本中修复)。然后,如果它确实意味着你想要的,那么你会得到一个错误,因为这两个match臂的类型不相等——一个是迭代器 over [i32; 1],另一个是迭代器 over [i32; 2]

    相反,您需要构建两个可能的迭代器,它们显然属于同一类型。有很多方法可以做到这一点,我选择的方法是使用Iterator::chaincombine once(i),一个返回单个元素的迭代器i,以及一个包含第二个元素(如果存在Option<i32>)的(实现) 。IntoIteratorj

    请注意,在第一match臂中,我写了看似无用的表达式.chain(None);这是为了使两个手臂具有相同的类型。编写相同内容的另一种方法是:

    let (i, opt_j) = match f {
        &Foo::A(i) => (i, None),
        &Foo::B(i, j) => (i, Some(j)),
    };
    once(i).chain(opt_j)
    

    在任何一种情况下,迭代器的类型都是std::iter::Chain<std::iter::Once<i32>, std::option::IntoIter<i32>>——您不需要确切地知道这一点,只需注意必须一个类型可以同时处理 theA(i)B(i, j)case。


推荐阅读