74

I want to call .map() on an array of enums:

enum Foo {
    Value(i32),
    Nothing,
}

fn main() {
    let bar = [1, 2, 3];
    let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
}

but the compiler complains:

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]`

How do I do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
rausch
  • 3,148
  • 2
  • 18
  • 27

10 Answers10

56

The issue is actually in collect, not in map.

In order to be able to collect the results of an iteration into a container, this container should implement FromIterator.

[T; n] does not implement FromIterator because it cannot do so generally: to produce a [T; n] you need to provide n elements exactly, however when using FromIterator you make no guarantee about the number of elements that will be fed into your type.

There is also the difficulty that you would not know, without supplementary data, which index of the array you should be feeding now (and whether it's empty or full), etc... this could be addressed by using enumerate after map (essentially feeding the index), but then you would still have the issue of deciding what to do if not enough or too many elements are supplied.

Therefore, not only at the moment one cannot implement FromIterator on a fixed-size array; but even in the future it seems like a long shot.


So, now what to do? There are several possibilities:

  • inline the transformation at call site: [Value(1), Value(2), Value(3)], possibly with the help of a macro
  • collect into a different (growable) container, such as Vec<Foo>
  • ...
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    I don't understand your point when you say "difficulty [to] know, without supplementary data, which index of the array you should be feeding now". – mcarton Mar 08 '20 at 20:48
  • 10
    While it's not possible to implement `FromIterator` for arrays, a function like that could exist in the standard library to return a `Result<[T; n], E>` instead. It could even be possible to collect into that type. – mcarton Mar 08 '20 at 20:49
  • 3
    And for ExactSizeIterator iterators, it would be straightforward to do the check before consuming elements. – saolof Aug 07 '20 at 01:26
  • 1
    @saolof `ExactSizeIterator` knows its size at runtime, not at compile time. For `[T; n]`, `n` needs to be known at compile time. Similarly for @mcarton's claim. – jbg Jun 11 '22 at 03:43
  • 1
    @jbg You may want to reread the claim made, and the return type of the proposed function. The function would do a runtime check that the length of the iterator is equal to the provided compile time constant n. – saolof Jun 12 '22 at 15:00
32

Update

This can work:

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

In newer version of rust, try_into is included in prelude, so it is not necessary to use std::convert::TryInto. Further, starting from 1.48.0, array support directly convert from Vec type, signature from stdlib source:

fn try_from(mut vec: Vec<T, A>) -> Result<[T; N], Vec<T, A>> {
    ...
}

Original Answer

as of rustc 1.42.0, if your element impl Copy trait, for simplicity, this just works:

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]

But I would just call it a workaround. You need to include std::convert::TryInto because the try_into method is defined in the TryInto trait.


Below is the signature checked when you call try_into as above, taken from the source. As you can see, that requires your type T implement Copy trait, so theoritically, it will copy all your elements once.

#[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)
    }
}
Bi Ao
  • 704
  • 5
  • 11
  • Not working, I get this error ```rust | 138 | .try_into() | ^^^^^^^^ the trait `std::convert::TryFrom<&[i32]>` is not implemented for `[i32; 64]` | = help: the following implementations were found: <&'a [T; N] as std::convert::TryFrom<&'a [T]>> <&'a mut [T; N] as std::convert::TryFrom<&'a mut [T]>> <[T; N] as std::convert::TryFrom<&[T]>> = note: required because of the requirements on the impl of `std::convert::TryInto<[i32; 64]>` for `&[i32]` ``` – Anonyme2000 Sep 03 '20 at 18:48
  • 2
    The explicit `.as_slice()` (and the `Copy` constraint) is [unnecessary](https://doc.rust-lang.org/std/primitive.array.html#impl-TryFrom%3CVec%3CT%3E%3E) in 1.48.0 – Anders Kaseorg Jan 08 '21 at 00:35
  • Playground link to the try_into() approach: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=54f2ab5121f91da441993730c4c52674 – j0057 Jan 27 '21 at 13:42
  • 6
    The indirection via `Vec` somewhat defeats the purpose. Collecting into an array is often desirable to avoid dynamic allocation. – TheOperator Mar 01 '22 at 10:38
  • An optimal solution would stop collecting as soon as N+1 elements are found. This seems like a non-starter to me. – cambunctious Oct 17 '22 at 19:58
22

While you cannot directly collect into an array for the reasons stated by the other answers, that doesn't mean that you can't collect into a data structure backed by an array, like an 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"));
    // use `.expect` instead if your type implements `Debug`
}

Pulling the array out of the ArrayVec returns a Result to deal with the case where there weren't enough items to fill it; the case that was discussed in the other answers.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
19

For your specific problem, Rust 1.55.0 allows you to directly map an array:

enum Foo {
    Value(i32),
    Nothing,
}

fn main() {
    let bar = [1, 2, 3];
    let foos = bar.map(Foo::Value);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
10

In this case you can use 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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
robitex
  • 503
  • 1
  • 4
  • 11
8

.collect() builds data structures that can have arbitrary length, because the iterator's item number is not limited in general. (Shepmaster's answer already provides plenty details there).

One possibility to get data into an array from a mapped chain without allocating a Vec or similar is to bring mutable references to the array into the chain. In your example, that'd look like this:

#[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);
}

The .zip() makes the iteration run over both bar and foos in lockstep -- if foos were under-allocated, the higher bars would not be mapped at all, and if it were over-allocated, it'd keep its original initialization values. (Thus also the Clone and Copy, they are needed for the [Nothing; 3] initialization).

chrysn
  • 810
  • 6
  • 19
5

You can actually define a Iterator trait extension to do this!

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]
}

Here's a playground link.

1I1III1
  • 167
  • 2
  • 10
4

This isn't possible because general arrays do not implement many traits. You can only collect into types which implement the FromIterator trait (see the list at the bottom of its docs).

This was a language limitation, since it is currently impossible to be generic over the length of an array and the length is part of its type. But, even if it were possible, it's very unlikely that FromIterator would be implemented on arrays because it'd have to panic if the number of items yielded wasn't exactly the length of the array.

ember arlynx
  • 3,129
  • 2
  • 20
  • 22
  • 5
    Arrays do implement traits, at least short ones. The standard library contains a lot of implementations for short arrays (up to 12 elements I think). The problem is that you can't make a generic implementation for all arrays, and that no array implements FromIterator. – Sebastian Redl Nov 06 '16 at 23:19
3

You may combine arrays map method with Iterator::next.

Example:

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
}
0

I ran into this problem myself — here's a workaround.

You can't use FromIterator, but you can iterate over the contents of a fixed-size object, or, if things are more complicated, indices that slice anything that can be accessed. Either way, mutation is viable.

For example, the problem I had was with an array of type [[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
}

If this is happening inside a small function, it's okay in my book. Either way, you were going to end up with a transformed value of identical type as the same one, so copying the whole thing first and then mutating is about the same amount of effort as referencing a value in a closure and returning some function of it.

Note that this only works if you plan to compute something that is going to be the same type, up to and including size/length. But that's implied by your use of Rust arrays. (Specifically, you could Value() your Foos or Nothing them as you like, and still be within type parameters for your array.)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
bright-star
  • 6,016
  • 6
  • 42
  • 81