How to filter a vector of custom structs?

It’s very important programming skill to learn how to create a minimal, reproducible example. Your problem can be reduced to this:

struct Vocabulary;

fn main() {
    let numbers = vec![Vocabulary];
    let other_numbers: Vec<Vocabulary> = numbers.iter().collect();
}

Let’s look at the error message for your case:

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

This says that a Vec<Vocabulary> cannot be built from an iterator of &Vocabulary. Do you see the difference? You have an iterator of references (&), not an iterator of values. How would Vec know how to convert your references into values?


How do you fix it? I don’t know what works best in your situation:

  1. Don’t iterate over references, iterate over the values themselves. The default choice requires that you have ownership of the vector. Use into_iter instead of iter:

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .into_iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    

    You could also drain the iterator if you have a mutable reference:

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .drain(..)
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    
  2. Duplicate the objects by cloning them. This requires that the type you are iterating on implements Clone. If you pair this with filtering, you should call cloned() after filtering and before calling collect() to avoid cloning something you discard.

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .cloned()
        .collect();
    
  3. Don’t collect values, collect a Vec of references. This requires that however you use the items afterwards can take an item by reference instead of by value:

    let the_vocabulary: Vec<&Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    

Note that I removed the redundant type specifiers (the turbofish ::<> on collect). You only need to specify the type of the variable or on collect, not both. In fact, all three examples could start with let the_vocabulary: Vec<_> to let the compiler infer the type inside the collection based on the iterator. This is the idiomatic style but I’ve kept the explicit types for demonstration purposes.


See also:

Leave a Comment