我想在一系列枚举上调用.map()
:
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
}
但编译器抱怨:
error[E0277]: the trait bound `[Foo; 3]: std::iter::FromIterator<Foo>` is not satisfied
--> src/main.rs:8:51
|
8 | let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
| ^^^^^^^ a collection of type `[Foo; 3]` cannot be built from an iterator over elements of type `Foo`
|
= help: the trait `std::iter::FromIterator<Foo>` is not implemented for `[Foo; 3]`
我该怎么做呢?
这个问题实际上是在collect
,而不是在map
。
为了能够将迭代结果收集到容器中,此容器应实现FromIterator
。
[T; n]
没有实现FromIterator
,因为它一般不能这样做:要生成一个[T; n]
,你需要准确提供n
元素,但是当使用FromIterator
时,你无法保证将被输入你的类型的元素数量。
如果没有补充数据,您现在还应该知道阵列的哪个索引(以及它是空的还是满的)等等,这也很难得到...这可以通过在enumerate
之后使用map
来解决(基本上是索引),但是如果没有提供足够的或太多的元素,你仍然会遇到决定该怎么做的问题。
因此,不仅在目前无法在固定大小的阵列上实现FromIterator
;但即使在未来,它似乎是一个长镜头。
那么,现在该怎么办?有几种可能性:
[Value(1), Value(2), Value(3)]
,可能是在宏的帮助下Vec<Foo>
在这种情况下,您可以使用Vec<Foo>
:
#[derive(Debug)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|&x| Foo::Value(x)).collect::<Vec<Foo>>();
println!("{:?}", foos);
}
这是不可能的,因为数组不实现任何特征。您只能收集实现FromIterator
特征的类型(请参阅its docs底部的列表)。
这是一种语言限制,因为它目前不可能在数组的长度上是通用的,并且长度是其类型的一部分。但是,即使有可能,FromIterator
也不太可能在阵列上实现,因为如果产生的项目数量不完全是阵列的长度,它就不得不恐慌。
.collect()
构建可以具有任意长度的数据结构,因为迭代器的项目编号通常不受限制。 (Shepmaster的答案已经在那里提供了很多细节)。
将数据从映射链中获取到数组而不分配Vec
或类似的一种可能性是将数组的可变引用引入链中。在您的示例中,这看起来像这样:
#[derive(Debug, Clone, Copy)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let mut foos = [Foo::Nothing; 3];
bar.iter().map(|x| Foo::Value(*x))
.zip(foos.iter_mut()).for_each(|(b, df)| *df = b);
}
.zip()
使得bar
和foos
在锁步中运行迭代 - 如果foos
分配不足,则更高的bar
s根本不会被映射,如果它被过度分配,它将保持其原始初始化值。 (因此克隆和复制也是[Nothing; 3]
初始化所需的)。
虽然您不能出于其他答案所述的原因直接收集到数组,但这并不意味着您无法收集到数组支持的数据结构,如ArrayVec
:
extern crate arrayvec;
use arrayvec::ArrayVec;
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos: ArrayVec<[_; 3]> = bar.iter().map(|x| Foo::Value(*x)).collect();
let the_array = foos.into_inner()
.unwrap_or_else(|_| panic!("Array was not completely filled"));
}
从ArrayVec
中拉出阵列返回一个Result
来处理没有足够的物品来填充它的情况;在其他答案中讨论过的案例。
into_inner
确实有一个警告:
注意:此函数可能会产生不成比例的大开销以将阵列移出,其性能不是最佳的。
因此,您可能希望将数据保留在原来的位置;你仍然避免堆分配。
我自己遇到了这个问题 - 这是一个解决方法。
你不能使用FromIterator
,但你可以遍历固定大小的对象的内容,或者,如果事情更复杂,那么切片可以访问任何可以访问的内容。无论哪种方式,突变都是可行的。
例如,我遇到的问题是[[usize; 2]; 4]
类型的数组:
fn main() {
// Some input that could come from another function and thus not be mutable
let pairs: [[usize; 2]; 4] = [[0, 0], [0, 1], [1, 1], [1, 0]];
// Copy mutable
let mut foo_pairs = pairs.clone();
for pair in foo_pairs.iter_mut() {
// Do some operation or other on the fixed-size contents of each
pair[0] += 1;
pair[1] -= 1;
}
// Go forth and foo the foo_pairs
}
如果这是在一个小函数中发生的,那么在我的书中就可以了。无论哪种方式,你最终会得到一个相同类型的转换值作为同一个,所以先复制整个事件然后变异与引用一个闭包中的值并返回它的一些函数大致相同的努力量。
请注意,这仅适用于计划计算类型相同的内容,包括大小/长度。但是你使用Rust数组暗示了这一点。 (具体来说,您可以根据需要Value()
你的Foo
或Nothing
,并且仍然在你的数组的类型参数范围内。)