web-dev-qa-db-fra.com

Comment itérer et filtrer un tableau?

J'essaie d'écrire un programme qui implique de filtrer et de replier des tableaux. J'utilise Le langage de programmation Rust, première édition comme référence, mais je ne comprends pas ce qui se passe lorsque je forme des itérateurs sur des tableaux. Voici un exemple:

fn compiles() {
    let range = (1..6);
    let range_iter = range.into_iter();
    range_iter.filter(|&x| x == 2);
}

fn does_not_compile() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    //13:34 error: the trait `core::cmp::PartialEq<_>` is not implemented for the type `&_` [E0277]
    array_iter.filter(|&x| x == 2);
}

fn janky_workaround() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    // Note the dereference in the lambda body
    array_iter.filter(|&x| *x == 2);
}

( terrain de jeu rouillé )

Dans la première fonction, je constate que l'itérateur de la plage ne prend pas la propriété. Je dois donc utiliser un &x dans lambda de filter, mais je ne comprends pas pourquoi le deuxième exemple avec le tableau se comporte différemment.

38
WillEngler

Dans de tels cas, il est très utile de forcer le compilateur à vous dire le type de la variable. Déclarons une erreur de type en affectant l’argument de fermeture à un type incompatible:

array_iter.filter(|x| { let () = x; true });

Cela échoue avec:

error[E0308]: mismatched types
  --> src/main.rs:12:33
   |
12 |     array_iter.filter(|x| { let () = x; true });
   |                                 ^^ expected &&{integer}, found ()
   |
   = note: expected type `&&{integer}`
              found type `()`

Nous savons maintenant que le type de x est un &&{integer} - une référence à une référence à à un type d’entier. Nous pouvons alors faire correspondre cela à la place:

fn hooray() {
    let array = [1, 4, 3, 2, 2];
    let array_iter = array.into_iter();
    array_iter.filter(|&&x| x == 2);
}

La question devient maintenant "pourquoi est-ce une référence à une référence"? La version courte est que le itérateur d'un tableau retourne les références (voir la partie type Item = &'a T). De plus, Iterator::filter transmet une référence à la fermeture pour empêcher le déplacement et la perte ultérieure de types non -Copy.

29
Shepmaster

Les tableaux sont de type [T; N] dans Rust, pour tout type d'élément T et un nombre constant N. C'est un tableau de taille fixe.

Rust ne met pas en œuvre les itérateurs par valeur pour les tableaux pour le moment. C'est pour cette raison que tous les tableaux contraignent les tranches (type [T]) et que les méthodes de tranche sont disponibles sur le tableau. Les tableaux reçoivent également l'itérateur de la tranche, appelé std::slice::Iter<'a, T> et comportant des éléments de type &'a T: itérant par référence!

C'est pourquoi into_iter() sur un Range<i32> génère un itérateur de i32 et into_iter() sur un [i32; 5] génère un itérateur de &i32.

Si vous avez besoin d'itérateurs par valeur pour les tableaux, ils ont été mis en œuvre dans l'écosystème plus large, voir (1) et (2) .

22
bluss

Comme Shepmaster et bluss l'ont dit, vous pouvez consulter la documentation pour le type de tableau , qui mentionne:

Les tableaux de tailles comprises entre 0 et 32 ​​implémentent les éléments suivants traits si le type d'élément le permet:

  • IntoIterator (implémenté pour &[T; N] et &mut [T; N])

Comme il est dit, cela ne concerne que les références et est reflété dans sa Item type: type Item = &'a T et type Item = &'a mut T.

1
Boris