Rust中枚举的展平向量

Rust中枚举的展平向量,rust,Rust,我试图在Rust中展平Enum的向量,但我遇到了一些问题: 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].in

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

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
}
enum Foo{
A(i32),
B(i32,i32),
}
fn main(){
设vf=vec![Foo::A(1),Foo::A(2),Foo::B(3,4)];
设vi:Vec=vf
.国际热核实验堆(iter)
.map(| f |匹配f{
Foo::A(i)=>[i].into_iter(),
Foo::B(i,j)=>[i,j].into_iter(),
})
.collect();//这不会编译
//我想要vi=[1,2,3,4]。vf必须仍然有效
}

我可以使用一个常规的for循环并将元素插入到一个现有的向量中,但这并不有趣。我想知道是否有一种更惯用的方法来执行此操作。

首先,您需要通过取消引用等方式将
i32
引用更改为自有值。然后,您可以使用
fold()
,通过内联数组绕过代理:

enum Foo{
A(i32),
B(i32,i32),
}
fn main(){
设vf=vec![Foo::A(1),Foo::A(2),Foo::B(3,4)];
设vi:Vec=vf
.国际热核实验堆(iter)
.fold(Vec::new(),| mut acc,f |{
匹配f{
Foo::A(i)=>acc.push(*i),
Foo::B(i,j)=>{
acc.push(*i);
附件推送(*j);
}
}
行政协调会
});
}

这里有一种生成迭代器的方法(而不必像基于
fold()
的解决方案那样生成向量)

使用std::iter::一次;
enum Foo{
A(i32),
B(i32,i32),
}
fn main(){
设vf=vec![Foo::A(1),Foo::A(2),Foo::B(3,4)];
设vi:Vec=vf
.国际热核实验堆(iter)
.平面图(| f|){
匹配f{
&Foo::A(i)=>一次(i).链(无),
&Foo::B(i,j)=>一次(i).chain(Some(j)),
}
})
.收集();
dbg!(vi);
}
这与您尝试的操作基本相同,但方式会成功。以下是我更改的部分,按照它们在代码中出现的顺序排列:

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

  • 我在
    匹配
    模式中使用了
    &
    。这是因为,由于您在向量上使用
    .iter()
    (这适合您的要求“
    vf
    必须仍然有效”),因此您有对枚举的引用,对枚举引用的模式匹配通常会给您对其元素的引用,但我们几乎肯定希望通过值来处理
    i32
    s。我还可以做一些其他的事情,比如对值使用
    *
    解引用操作符,但这是简洁而整洁的

  • 您试图
    。插入数组中。不幸的是,在当前的Rust中,这并不能满足您的需要,并且您实际上无法返回迭代器,原因有点尴尬()。然后,如果它确实意味着你想要的,那么你会得到一个错误,因为两个
    匹配
    臂有不同的类型-一个是
    [i32;1]
    上的迭代器,另一个是
    [i32;2]
    上的迭代器

    相反,您需要构建两个可能的迭代器,它们显然属于同一类型。有很多方法可以做到这一点,我选择的方法是使用组合,一个迭代器返回单个元素
    I
    ,一个
    选项
    (实现
    IntoIterator
    ),其中包含第二个元素
    j
    ,如果它存在的话

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

    let(i,opt_j)=匹配f{
    &Foo::A(i)=>(i,无),
    &Foo::B(i,j)=>(i,Some(j)),
    };
    一次(i).链(选择j)
    
    在这两种情况下,迭代器的类型都是
    std::iter::Chain
    ——您不需要确切了解这一点,只需注意必须有一个类型同时处理
    a(i)
    B(i,j)
    情况


  • 您可以在
    Foo
    上实现
    IntoIterator
    ,然后实现链式迭代器,但老实说,在这里很难比
    for
    循环和
    匹配更简洁、更清晰。
    
    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()
            .fold(Vec::new(), |mut acc, f| {
                match f {
                    Foo::A(i) => acc.push(*i),
                    Foo::B(i, j) => {
                        acc.push(*i);
                        acc.push(*j);
                    }
                }
                acc
            });
    
    }