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?
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.
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.
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:
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();
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();
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With