J'essaie de filtrer un Vec<Vocabulary>
où Vocabulary
est un struct
personnalisé, qui contient lui-même un struct
VocabularyMetadata
et un Vec<Word>
:
#[derive(Serialize, Deserialize)]
pub struct Vocabulary {
pub metadata: VocabularyMetadata,
pub words: Vec<Word>
}
C'est pour gérer un itinéraire dans une application Web, où l'itinéraire ressemble à ceci: /Word/<vocabulary_id>/<Word_id>
.
Voici mon code actuel essayant de filter
le Vec<Vocabulary>
:
let the_vocabulary: Vec<Vocabulary> = vocabulary_context.vocabularies.iter()
.filter(|voc| voc.metadata.identifier == vocabulary_id)
.collect::<Vec<Vocabulary>>();
Cela ne fonctionne pas. L'erreur que j'obtiens est:
the trait `std::iter::FromIterator<&app_structs::Vocabulary>` is not implemented for `std::vec::Vec<app_structs::Vocabulary>` [E0277]
Je ne sais pas comment implémenter un FromIterator
, ni pourquoi cela serait nécessaire. Dans une autre route dans la même application web, même fichier, je fais ce qui suit, qui fonctionne:
let result: Vec<String> = vocabulary_context.vocabularies.iter()
.filter(|voc| voc.metadata.identifier.as_str().contains(vocabulary_id))
.map(encode_to_string)
.collect::<Vec<String>>();
result.join("\n\n") // returning
Il semble donc que String
implémente FromIterator
.
Cependant, je ne comprends pas, pourquoi je ne peux pas simplement récupérer les éléments de Vec
à partir de la méthode filter
ou collect
.
Comment puis-je filter
mon Vec
et obtenir simplement les éléments du Vec<Vocabulary>
, pour lequel la condition est vraie?
C'est une compétence de programmation très importante pour apprendre à créer un exemple minimal et reproductible . Votre problème peut être réduit à ceci:
struct Vocabulary;
fn main() {
let numbers = vec![Vocabulary];
let other_numbers: Vec<Vocabulary> = numbers.iter().collect();
}
Regardons le message d'erreur pour votre cas:
error[E0277]: a collection of type `std::vec::Vec<Vocabulary>` cannot be built from an iterator over elements of type `&Vocabulary` --> src/main.rs:5:57 | 5 | let other_numbers: Vec<Vocabulary> = numbers.iter().collect(); | ^^^^^^^ a collection of type `std::vec::Vec<Vocabulary>` cannot be built from `std::iter::Iterator<Item=&Vocabulary>` | = help: the trait `std::iter::FromIterator<&Vocabulary>` is not implemented for `std::vec::Vec<Vocabulary>`
Cela signifie qu'un Vec<Vocabulary>
Ne peut pas être construit à partir d'un itérateur de &Vocabulary
. Voyez-vous la différence? Vous avez un itérateur de références (&
), pas un itérateur de valeurs. Comment Vec
saurait-il convertir vos références en valeurs?
Comment le corrigez-vous? Je ne sais pas ce qui fonctionne le mieux dans votre situation:
Ne parcourez pas les références, parcourez les valeurs elles-mêmes. Le choix par défaut nécessite que vous soyez propriétaire du vecteur. Utilisez into_iter
Au lieu de iter
:
let the_vocabulary: Vec<Vocabulary> = vocabulary_context
.vocabularies
.into_iter()
.filter(|voc| voc.metadata.identifier == vocabulary_id)
.collect();
Vous pouvez également drain l'itérateur si vous avez une référence mutable:
let the_vocabulary: Vec<Vocabulary> = vocabulary_context
.vocabularies
.drain(..)
.filter(|voc| voc.metadata.identifier == vocabulary_id)
.collect();
Dupliquez les objets en les clonant. Cela nécessite que le type sur lequel vous itérez implémente Clone
. Si vous associez cela au filtrage, vous devez appeler cloned()
après le filtrage et avant d'appeler collect()
pour éviter de cloner quelque chose que vous jetez.
let the_vocabulary: Vec<Vocabulary> = vocabulary_context
.vocabularies
.iter()
.filter(|voc| voc.metadata.identifier == vocabulary_id)
.cloned()
.collect();
Ne collectez pas de valeurs, collectez un Vec
de références. Cela nécessite toutefois que vous utilisiez les éléments par la suite pour pouvoir prendre un élément par référence plutôt que par valeur:
let the_vocabulary: Vec<&Vocabulary> = vocabulary_context
.vocabularies
.iter()
.filter(|voc| voc.metadata.identifier == vocabulary_id)
.collect();
Notez que j'ai supprimé les spécificateurs de type redondants (le turbofish ::<>
Sur collect
). Il vous suffit de spécifier le type de la variable ou sur collect
, pas les deux. En fait, les trois exemples peuvent commencer par let the_vocabulary: Vec<_>
Pour permettre au compilateur de déduire le type à l'intérieur de la collection en fonction de l'itérateur. C'est le style idiomatique mais j'ai gardé les types explicites à des fins de démonstration.
Voir également: