我如何收集到阵列?

rau*_*sch 15 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]>();
}
Run Code Online (Sandbox Code Playgroud)

但编译器抱怨:

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]`
Run Code Online (Sandbox Code Playgroud)

我该怎么做呢?

Jea*_*ant 28

对于您的具体问题,Rust 1.55.0 允许您直接映射数组:

enum Foo {
    Value(i32),
    Nothing,
}

fn main() {
    let bar = [1, 2, 3];
    let foos = bar.map(Foo::Value);
}
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 23

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

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

[T; n]没有实现,FromIterator因为它通常不能这样做:生成一个[T; n]你需要n准确提供元素,但是在使用时FromIterator你不能保证将被输入你的类型的元素数量.

如果没有补充数据,您现在还应该知道阵列的哪个索引(以及它是空的还是满的)等等,这也很难以解决...这可以通过使用enumerateafter map(基本上为索引提供)来解决),但是如果没有提供足够的或过多的元素,你仍然会有决定做什么的问题.

因此,不仅在目前无法FromIterator在固定大小的阵列上实现; 但即使在未来,它似乎是一个长镜头.


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

  • 在调用站点内联转换:[Value(1), Value(2), Value(3)]可能在宏的帮助下
  • 收集到不同的(可生长的)容器中,例如 Vec<Foo>
  • ...

  • 虽然不可能为数组实现“FromIterator”,但标准库中可能存在类似的函数来返回“Result&lt;[T;” n], E&gt;` 代替。甚至有可能收集到那种类型。 (3认同)
  • 对于 ExactSizeIterator 迭代器,在使用元素之前进行检查会很简单。 (3认同)

She*_*ter 13

虽然由于其他答案所述的原因,您不能直接收集到数组中,但这并不意味着您不能收集到由数组支持的数据结构中,例如ArrayVec

use arrayvec::ArrayVec; // 0.7.0
use std::array;

enum Foo {
    Value(i32),
    Nothing,
}

fn main() {
    let bar = [1, 2, 3];
    let foos: ArrayVec<_, 3> = array::IntoIter::new(bar).map(Foo::Value).collect();
    let the_array = foos
        .into_inner()
        .unwrap_or_else(|_| panic!("Array was not completely filled"));
}
Run Code Online (Sandbox Code Playgroud)

将数组从ArrayVec返回 a 中拉出来Result以处理没有足够项来填充它的情况;在其他答案中讨论的案例。


rob*_*tex 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);
}
Run Code Online (Sandbox Code Playgroud)


小智 6

.collect()构建可以具有任意长度的数据结构,因为迭代器的项目编号通常不受限制。(Shepmaster 的回答已经在那里提供了很多细节)。

在不分配 aVec或类似值的情况下将数据从映射链获取到数组中的一种可能性是将对该数组的可变引用引入链中。在您的示例中,它看起来像这样:

#[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);
}
Run Code Online (Sandbox Code Playgroud)

.zip()使得迭代在两个barfoos锁步中运行——如果foos分配不足,则bar根本不会映射较高的s,如果分配过多,它将保留其原始初始化值。(因此也是克隆和复制,它们是[Nothing; 3]初始化所必需的)。


Ang*_*ros 6

您可以将数组map方法与Iterator::next.

例子:

fn iter_to_array<Element, const N: usize>(mut iter: impl Iterator<Item = Element>) -> [Element; N] {
    // Here I use `()` to make array zero-sized -> no real use in runtime.
    // `map` creates new array, which we fill by values of iterator.
    let res = [(); N].map(|_| iter.next().unwrap());
    // Ensure that iterator finished
    assert!(matches!(iter.next(), None));
    res
}
Run Code Online (Sandbox Code Playgroud)

UPD

Rust 1.63 发布后,可以使用std::array::from_fn以下方法:

fn iter_to_array<Element, const N: usize>(mut iter: impl Iterator<Item = Element>) -> [Element; N] {
    // Here I use `()` to make array zero-sized -> no real use in runtime.
    // `map` creates new array, which we fill by values of iterator.
    let res: [_; N] = std::array::from_fn(|_| iter.next().unwrap());
    // Ensure that iterator finished
    assert!(matches!(iter.next(), None));
    res
}
Run Code Online (Sandbox Code Playgroud)


Cor*_*son 5

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

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

  • 数组确实实现了特征,至少是短的。标准库包含许多短数组的实现(我认为最多 12 个元素)。问题是你不能为所有数组做一个通用的实现,而且没有数组实现 FromIterator。 (4认同)

小智 5

您实际上可以定义一个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]
}
Run Code Online (Sandbox Code Playgroud)

这是一个游乐场链接


Bi *_* Ao 5

从 rustc 1.42.0 开始,如果您的元素具有 implCopy特征,为简单起见,这仅适用:

use std::convert::TryInto;

...

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

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


以下是您在调用时检查的签名try_into,取自source。如您所见,这需要您的类型T实现Copytrait,因此理论上,它将复制您的所有元素一次。

#[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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 通过“Vec&lt;T&gt;”的间接在某种程度上违背了目的。通常需要收集到数组中以避免动态分配。 (9认同)
  • 显式的 `.as_slice()` (和 `Copy` 约束)是[不必要的](https://doc.rust-lang.org/std/primitive.array.html#impl-TryFrom%3CVec%3CT%3E %3E) 在 1.48.0 中 (2认同)