Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter a vector of custom structs in Rust?

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?

like image 583
Zelphir Kaltstahl Avatar asked Jun 20 '17 20:06

Zelphir Kaltstahl


People also ask

What is VEC rust?

It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity. Vectors ensure they never allocate more than isize::MAX bytes.

How do you get a value from a vector in Rust?

Rust provides us with two methods to access elements in a vector. We can access values with either the indexer, or with the get() method.


1 Answers

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:

  • What is the difference between iter and into_iter?
  • When should I use `drain` vs `into_iter`?
like image 174
Shepmaster Avatar answered Sep 18 '22 14:09

Shepmaster