我如何收集到阵列?

时间:2014-11-05 12:35:16

标签: rust

我想在枚举数组上调用.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]`

我该怎么做?

9 个答案:

答案 0 :(得分:21)

问题实际上在collect,而不在map

为了能够将迭代结果收集到容器中,此容器应实现FromIterator

[T; n]未实现FromIterator,因为它通常无法实现:生成[T; n]您需要准确提供n个元素,但是在使用{{1}时您无法保证将输入您的类型的元素数量。

如果没有补充数据,你现在还应该知道你应该给阵列的哪个索引(以及它是空的还是满的)等等,你也不会知道......这可以通过使用{{1在FromIterator之后(主要是为索引提供),但是如果提供的元素不够或者提供的元素太多,你仍然会遇到决定该怎么做的问题。

因此,目前不仅在固定大小的数组上实现enumerate;但即使在未来,这似乎是一个长镜头。


那么,现在该怎么办?有几种可能性:

  • 在呼叫网站上内联转换:map,可能借助宏
  • 收集到另一个(可增长的)容器,例如FromIterator
  • ...

答案 1 :(得分:6)

在这种情况下,您可以使用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);
}

答案 2 :(得分:4)

这是不可能的,因为数组不实现任何特征。您只能收集实现FromIterator特征的类型(请参阅its docs底部的列表)。

这是一种语言限制,因为它目前不可能在数组的长度上是通用的,并且长度是其类型的一部分。但是,即使它 可能,FromIterator也不太可能在数组上实现,因为如果产生的项目数量不完全是长度,则必须恐慌。阵列。

答案 3 :(得分:3)

虽然您无法根据其他答案所述的原因直接收集到数组,但这并不意味着您无法通过数组收集到支持的数据结构,例如{ {3}}:

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来处理没有足够的项目来填充它的情况;在其他答案中讨论的案例。

ArrayVec确实有一个警告:

  

注意:此函数可能会产生不成比例的大量开销以将阵列移出,其性能不是最佳的。

因此,您可能希望将数据保留在原来的位置;你仍然避免了堆分配。

答案 4 :(得分:3)

.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()使迭代在barfoos上同步进行-如果foos分配不足,则较高的bar将根本不会被映射,并且如果它被过度分配,它将保留其原始初始化值。 (因此还有克隆和复制,[Nothing; 3]初始化需要它们。)

答案 5 :(得分:3)

您实际上可以定义一个Iterator特征扩展来做到这一点!

use std::convert::AsMut;
use std::default::Default;

trait CastExt<T, U: Default + AsMut<[T]>>: Sized + Iterator<Item = T> {
    fn cast(mut self) -> U {
        let mut out: U = U::default();
        let arr: &mut [T] = out.as_mut();
        for i in 0..arr.len() {
            match self.next() {
                None => panic!("Array was not filled"),
                Some(v) => arr[i] = v,
            }
        }
        assert!(self.next().is_none(), "Array was overfilled");
        out
    }
}

impl<T, U: Iterator<Item = T>, V: Default + AsMut<[T]>> CastExt<T, V> for U { }

fn main () {
    let a: [i32; 8] = (0..8).map(|i| i * 2).cast();
    println!("{:?}", a); // -> [0, 2, 4, 6, 8, 10, 12, 14]
}

这里是playground link

答案 6 :(得分:3)

从rustc 1.42.0开始,如果您的元素具有Copy特质,为简单起见,这将起作用:

use std::convert::TryInto;

...

let array: [T; N]  = something_iterable.[into_]iter()
    .collect::<Vec<T>>()
    .as_slice()
    .try_into()
    .unwrap()
              collect            as_slice          try_into + unwrap()
Iterator<T>   ------>   Vec<T>   ------->   &[T]   ------------------>   [T]

但是我只能称之为解决方法。 您需要包含std::convert::TryInto,因为try_into方法是在TryInto特征中定义的。


下面是当您如上所述从source调用try_into时检查的签名。如您所见,这要求您的类型T实现Copy特征,因此从理论上讲,它将复制所有元素一次。

#[stable(feature = "try_from", since = "1.34.0")]
impl<T, const N: usize> TryFrom<&[T]> for [T; N]
where
    T: Copy,
    [T; N]: LengthAtMost32,
{
    type Error = TryFromSliceError;

    fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> {
        <&Self>::try_from(slice).map(|r| *r)
    }
}

答案 7 :(得分:2)

每晚使用 array_map 功能标志,您可以:

#![feature(array_map)]
fn main() {
    let bar = [1, 2, 3];
    let foos = bar.map(|x| Foo::Value(x));
}

答案 8 :(得分:0)

我自己遇到了这个问题 - 这是一个解决方法。

您不能使用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() FooNothing,并且仍然在数组的类型参数范围内。)