51

I am trying to filter a Vec<Vocabulary> where Vocabulary is a custom struct, which itself contains a struct VocabularyMetadata and a Vec<Word>:

#[derive(Serialize, Deserialize)]
pub struct Vocabulary {
    pub metadata: VocabularyMetadata,
    pub words: Vec<Word>
}

This is for handling a route in a web application, where the route looks like this: /word/<vocabulary_id>/<word_id>.

Here is my current code trying to filter the Vec<Vocabulary>:

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

This does not work. The error I get is:

 the trait `std::iter::FromIterator<&app_structs::Vocabulary>` is not implemented for `std::vec::Vec<app_structs::Vocabulary>` [E0277]

I don't know how to implement any FromIterator, nor why that would be necessary. In another route in the same web app, same file I do the following, which works:

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

So it seems that String implements FromIterator.

However, I don't get, why I cannot simple get back the Elements of the Vec from the filter or collect method.

How can I filter my Vec and simply get the elements of the Vec<Vocabulary>, for which the condition is true?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Zelphir Kaltstahl
  • 5,722
  • 10
  • 57
  • 86

1 Answers1

113

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    I see now the little but important `&`. Totally overlooked that. I don't understand the problem with using the reference to a `Vocabulary` and just giving me the object at the reference. Is it about unknown size of the object (struct, containing another Vec etc.)? For me only the (1) first solution works, because (2) `std::clone::Clone` is also not satisfied and (3) is still mismatched type: `app_structs::Vocabulary` vs `&app_structs::Vocabulary`. I'll read up on `into_iter` again. I read it was destructive and actually wanted a copy of the contained elements, so cloning might make sense. – Zelphir Kaltstahl Jun 20 '17 at 20:42
  • *and just giving me the object at the reference* — What would you propose would happen to the thing that currently has the object? If you took ownership, then that would be left in an undefined state and any further usage of it would cause memory unsafety. – Shepmaster Jun 20 '17 at 21:44
  • Hm, that's strange. I thought because all of this happens in the same scope (ok maybe I borrow it in between to "subscopes", but they return ownership anyway), I would not need to worry about such a thing. Couldn't the compiler figure that out? I mean filter and so on are predefined functions after all, which it could reason about. Are you sure it is not about unknown size rather than ownership? – Zelphir Kaltstahl Jun 20 '17 at 22:17
  • 1
    @Zelphir, the compiler figured out that you want to get `Vec` from iterator that provides `&Vocabulary` references. `filter` is just a usual method of a trait. All the compiler knows about it is its signature `fn collect(self) -> B where B: FromIterator`, so it checks whether type `Vec` implements trait `FromIterator<&Vocabulary>`, finds out that it does not and reports error. – red75prime Jun 21 '17 at 07:36
  • 1
    i am new to rust. could you explain or show me some resources what this means `|voc|` i come from JS and have never saw something like that – bill.gates Dec 11 '20 at 08:15
  • 1
    @Ifaruki https://doc.rust-lang.org/book/ch13-01-closures.html – Shepmaster Dec 14 '20 at 18:52