我试图在 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
}
我可以只使用常规的 for 循环并将元素插入现有的向量中,但这并不有趣。我想知道是否有更惯用的 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);
}
这基本上与您尝试的相同,但以一种会成功的方式。以下是我更改的部分,按照它们在代码中出现的顺序排列:
我使用了
.flat_map()
而不是.map()
.flat_map
接受一个函数,该函数返回一个迭代器 并生成该迭代器的元素(“展平”),而.map()
只会给出迭代器。我使用了
&
在match
模式。这是因为,因为您正在使用.iter()
在向量上(这适合您的要求“vf
必须仍然有效”),您有对枚举的引用,并且对枚举引用的模式匹配通常会给您它的元素的引用,但我们几乎肯定想要处理i32
s 按值代替。我还可以做其他几件事,例如使用*
取而代之的是在值上取消引用运算符,但这样简洁明了。您尝试了
.into_iter()
数组。不幸的是,在当前的 Rust 中,这并没有做你想做的事情,你实际上不能返回那个迭代器,因为有些尴尬的原因 ( which will be fixed in an upcoming Rust version )。然后,如果它确实是你想要的,那么你会得到一个错误,因为两个match
arms 有不同的类型——一个是[i32; 1]
上的迭代器另一个是[i32; 2]
上的迭代器.相反,您需要构建两个明显相同 类型的迭代器。有很多方法可以做到这一点,我选择的方法是使用
Iterator::chain
结合once(i)
,一个返回单个元素的迭代器i
, 与Option<i32>
(实现IntoIterator
)包含第二个元素j
如果存在。请注意,在第一个
match
arm 我写了看似没用的表达式.chain(None)
;这是为了使两个 ARM 具有相同的类型。另一种编写相同内容的方法可以说更清晰,因为它不会重复必须相同的代码,它是: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>>
— 您不需要确切地知道这一点,只需注意必须 一个类型来处理A(i)
和B(i, j)
例。
关于rust - Rust 中枚举的展平向量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67346301/