如何在Rust中过滤自定义结构的向量?
我正在尝试过滤Vec<Vocabulary>
,其中Vocabulary
是自定义struct
,该自定义struct
本身包含struct
VocabularyMetadata
和Vec<Word>
:
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>
}
这用于处理Web应用程序中的路由,该路由如下所示:/word/<vocabulary_id>/<word_id>
.
This is for handling a route in a web application, where the route looks like this: /word/<vocabulary_id>/<word_id>
.
这是我当前的尝试filter
Vec<Vocabulary>
的代码:
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]
我不知道如何实现任何FromIterator
,也不知道为什么需要这样做.在同一Web应用程序的另一条路由中,我执行以下文件,该文件有效:
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
所以看来String
实现了FromIterator
.
但是,我不明白,为什么我不能简单地从filter
或collect
方法中取回Vec
的元素.
However, I don't get, why I cannot simple get back the Elements of the Vec
from the filter
or collect
method.
如何filter
我的Vec
并仅获取条件为true的Vec<Vocabulary>
的元素?
How can I filter
my Vec
and simply get the elements of the Vec<Vocabulary>
, for which the condition is true?
学习如何创建最小的,可复制的示例非常重要,这是编程技能.您的问题可以归结为:
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>`
这表示不能从&Vocabulary
的迭代器构建Vec<Vocabulary>
.你看得到差别吗?您具有引用的迭代器(&
),不是值的迭代器. Vec
如何知道如何将您的引用转换为值?
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:
-
不要遍历引用,而是遍历值本身.默认选择要求您拥有向量的所有权.使用
into_iter
代替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();
通过克隆对象来复制它们.这要求您要在工具Clone
上迭代的类型.如果将此选项与过滤配对,则应在过滤之后且在调用collect()
之前调用cloned()
,以避免克隆您丢弃的内容.
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();
不收集值,收集引用的Vec
.这就要求,但是您以后使用这些项目时,可以通过引用而不是按值来获取一个项目:
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();
请注意,我删除了冗余类型说明符(collect
上的turbofish ::<>
).您只需要指定变量的类型或在collect
上指定,而不必同时在两者上指定.实际上,所有三个示例都可以以let the_vocabulary: Vec<_>
开头,以使编译器根据迭代器来推断集合内的类型.这是惯用的样式,但为了演示起见,我保留了明确的类型.
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.
另请参阅:
- What is the difference between iter and into_iter?
- When should I use `drain` vs `into_iter`?