Как я могу собрать в массив?

Я хочу вызвать .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]`

Как это сделать?

Ответ 1

Проблема на самом деле находится в collect, а не в map.

Чтобы иметь возможность собирать результаты итерации в контейнер, этот контейнер должен реализовывать FromIterator.

[T; n] не реализует FromIterator, потому что он не может сделать это в целом: для создания [T; n] вам необходимо точно указать элементы n, однако при использовании FromIterator вы не гарантируете количество элементов которые будут загружены в ваш тип.

Существует также трудность, которую вы не знали бы без дополнительных данных, какой индекс массива вы должны сейчас кормить (и пустым или полным) и т.д.... это можно решить, используя enumerate после map (по существу, подавая индекс), но тогда у вас все еще будет проблема решить, что делать, если недостаточно или слишком много элементов.

Следовательно, не только в настоящий момент нельзя реализовать FromIterator в массиве фиксированного размера; но даже в будущем это кажется длинным выстрелом.


Итак, что теперь делать? Существует несколько возможностей:

  • встроить преобразование на сайт вызова: [Value(1), Value(2), Value(3)], возможно, с помощью макроса
  • собирать в другой (растущий) контейнер, например Vec<Foo>
  • ...

Ответ 2

В этом случае вы можете использовать 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);
}

Ответ 3

Это невозможно, потому что массивы не реализуют никаких признаков. Вы можете собирать только те типы, которые реализуют черту FromIterator (см. Список внизу его документы).

Это ограничение языка, поскольку в настоящее время невозможно быть общим по длине массива, а длина является частью его типа. Но, даже если это было возможно, очень маловероятно, что FromIterator будет реализован на массивах, потому что он должен был бы паниковать, если количество элементов, полученных, не было точно длиной массива.

Ответ 4

Хотя вы не можете напрямую собирать данные в массив по причинам, указанным в других ответах, это не означает, что вы не можете собирать данные в структуру данных, поддерживаемую массивом, например ArrayVec:

use arrayvec::ArrayVec; // 0.4.10

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 массива из ArrayVec возвращает Result чтобы иметь дело со случаем, когда не было достаточно элементов для его заполнения; случай, который обсуждался в других ответах.

into_inner имеет оговорку:

Примечание. Эта функция может потребовать непропорционально больших накладных расходов для перемещения массива, ее производительность не является оптимальной.

Из-за этого вы можете просто оставить данные там, где они есть; Вы бы все-таки избежали выделения кучи.

Ответ 5

.collect() создает структуры данных, которые могут иметь произвольную длину, потому что номер элемента итератора в целом не ограничен. (Ответ Шепмастера уже дает много подробностей там).

Одна возможность получить данные в массив из отображенной цепочки, не выделяя 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 в lockstep - если foos был перераспределен, верхний bar вообще не будет отображаться, а если он перераспределен, он сохранит свою первоначальную инициализацию ценности. (Таким образом, также Clone и Copy, они необходимы для инициализации [Nothing; 3]).

Ответ 6

Я столкнулся с этой проблемой сам - здесь обходной путь.

Вы не можете использовать 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 их по своему усмотрению и все еще находиться в параметрах типа для вашего массива.)

Ответ 7

Чтобы преобразовать из Vec<T> в [T], вы можете написать:

vec.as_ref();

Обратите внимание, что результат не имеет размера, а функции, ожидающие [T], также могут принимать Vec<T>.