2014-11-05 3 views
10

Ich möchte .map() auf eine Reihe von Aufzählungen nennen:Wie sammle ich in einem Array?

enum Foo { 
    Value(i32), 
    Nothing, 
} 

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

aber der Compiler beschwert sich:

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

Wie mache ich das?

Antwort

4

Dies ist nicht möglich, da Arrays keine Züge implementieren. Sie können nur in Typen sammeln, die das Merkmal FromIterator implementieren (siehe die Liste am Ende von its docs).

Dies ist eine Sprache Einschränkung, da es derzeit unmöglich ist, über die Länge eines Arrays generisch zu sein und die Länge ist Teil seiner Art. Aber selbst wenn es war möglich, dann ist es sehr unwahrscheinlich, dass FromIterator auf Arrays implementiert werden würde, weil es müßte Panik, wenn die Anzahl der Elemente ergab war nicht gerade die Länge des Arrays.

+2

Arrays implementieren Merkmale, zumindest kurze. Die Standardbibliothek enthält viele Implementierungen für kurze Arrays (bis zu 12 Elemente, denke ich). Das Problem besteht darin, dass Sie keine generische Implementierung für alle Arrays erstellen können und dass kein Array FromIterator implementiert. –

6

In diesem Fall können Sie Vec<Foo> verwenden:

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

Das Problem tatsächlich in collect ist, nicht in map.

Um der Lage sein, die Ergebnisse einer Iteration in einen Behälter zu sammeln, sollte dieser Behälter FromIterator implementieren.

[T; n] nicht implementiert FromIterator, weil es nicht so allgemein tun kann: ein [T; n] Sie n Elemente genau zur Verfügung stellen müssen, um zu produzieren, aber wenn FromIterator verwenden Sie keine Garantie über die Anzahl der Elemente zu machen, die in Ihre Art zugeführt werden.

Es gibt auch die Schwierigkeit, dass Sie ohne zusätzliche Daten nicht wissen würden, welcher Index des Arrays Sie jetzt füttern sollten (und ob er leer oder voll ist), etc ... dies könnte mit Hilfe von enumerate behoben werden map (im Wesentlichen den Index zu füttern), aber dann hätten Sie immer noch das Problem zu entscheiden, was zu tun ist, wenn nicht genug oder zu viele Elemente geliefert werden.

Daher ist nicht nur im Moment man kann nicht auf einem FromIterator feste Größe Array implementieren; aber auch in Zukunft scheint es so, als wäre es ein langer Schuss.


Also, was nun zu tun? Es gibt mehrere Möglichkeiten:

  • inline die Transformation bei Aufrufort: [Value(1), Value(2), Value(3)], möglicherweise mit Hilfe eines Makro
  • sammeln in einen anderen (Erweiterbare) Behälter, wie Vec<Foo>
  • ...
0

ich lief in dieses Problem selbst - ist hier eine Abhilfe.

Sie können FromIterator nicht verwenden, aber Sie können über den Inhalt eines Objekts fester Größe iterieren, oder, wenn die Dinge komplizierter sind, Indizes, die alles zerschneiden, auf das zugegriffen werden kann. In jedem Fall ist Mutation lebensfähig.

Zum Beispiel das Problem, das ich war der Typ mit einer Reihe hatte [[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 
} 

Wenn dies in einer kleinen Funktion geschieht, es ist in Ordnung in meinem Buch. Wie auch immer, Sie würden am Ende einen transformierten Wert identischen Typs erhalten, also kopieren Sie das Ganze zuerst und dann mutieren Sie über den gleichen Aufwand wie das Referenzieren eines Werts in einem Abschluss und das Zurückgeben einer Funktion davon .

Beachten Sie, dass dies nur funktioniert, wenn Sie planen, etwas zu berechnen, das den gleichen Typ hat, bis einschließlich Größe/Länge. Aber das wird durch Ihre Verwendung von Rust-Arrays impliziert. (. Insbesondere könnten Sie Ihre Foo s oder Nothing sie Value(), wie Sie möchten, und für Ihr Array innerhalb Typparameter sein noch)

-2

Zur Transformation von Vec<T> zu [T], können Sie schreiben:

vec.as_ref(); 

Hinweis dass das Ergebnis nicht in der Größe und Funktionen erwartet [T] könnte auch Vec<T> akzeptieren.

+0

Dies erzeugt kein * Array *, wie das OP anfordert; [es erstellt ein * slice * ('& [T]')] (https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-AsRef <[T]>) – Shepmaster

0

Sie können zwar nicht direkt für die Gründe, die von den anderen Antworten angegeben in ein Array sammeln, das bedeutet nicht, dass Sie nicht in eine Datenstruktur durch eine Anordnung gesichert sammeln können, wie ein 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")); 
} 

Wenn Sie das Array aus dem ArrayVec ziehen, wird Result zurückgegeben, um den Fall zu behandeln, in dem nicht genügend Elemente vorhanden waren, um es zu füllen. der Fall, der in den anderen Antworten diskutiert wurde.

into_inner hat eine Einschränkung:

Hinweis: Diese Funktion unproportional große Overhead entstehen kann das Array aus, um seine Leistung zu bewegen, ist nicht optimal.

Aus diesem Grund möchten Sie die Daten nur dort lassen, wo sie sind. Sie hätten trotzdem die Heap-Zuweisung vermieden.

Verwandte Themen