我试图将锈蚀中的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循环并将元素插入到现有的向量中,但这并不有趣。我想知道是否有一种更地道的生锈方式。
发布于 2021-05-01 15:45:20
下面是一种生成迭代器的方法(而不是像基于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。我还可以做一些其他的事情,比如在值上使用*取消引用操作符,但是这是简洁和整洁的。.into_iter()一个数组。不幸的是,在当前的Rust中,由于一些尴尬的原因(将在即将发布的锈菌版本中修复),这并不能满足您的需要,而且您实际上无法返回那个迭代器。然后,如果它确实是您想要的,那么您就会得到一个错误,因为两个match臂有不同的类型--一个是[i32; 1]上的迭代器,另一个是[i32; 2]上的迭代器。
相反,您需要构建两个可能的迭代器,它们显然是相同类型的。有很多方法可以做到这一点,我选择的方法是使用Iterator::chain将返回单个元素i的迭代器once(i)与包含第二个元素j (如果存在的话包含第二个元素j)的Option<i32> (实现IntoIterator)结合起来。
注意,在第一个match arm中,我编写了一个看似无用的表达式.chain(None);这使得这两个臂具有相同的类型。另一种编写相同东西的方法,可以说更清楚,因为它不重复必须相同的代码,是:
设(i,opt_j) =匹配f{ &Foo::A(i) => (i,None),&Foo:B (i,j) => (I,Some(j)),};一次(I).chain(Opt_j)
在这两种情况下,迭代器的类型都是std::iter::Chain<std::iter::Once<i32>, std::option::IntoIter<i32>> --您不需要确切地知道这一点,只需注意,必须有一种同时处理A(i)和B(i, j)情况的类型。发布于 2021-05-01 13:35:20
首先,您需要将i32引用更改为拥有的值,例如取消引用。然后,您可以通过使用fold()绕过内联数组的代理。
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
});
}https://stackoverflow.com/questions/67346301
复制相似问题