如何在Rust中过滤自定义结构的向量? [英] How to filter a vector of custom structs in Rust?

查看:187
本文介绍了如何在Rust中过滤自定义结构的向量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试过滤Vec<Vocabulary>,其中Vocabulary是自定义struct,该自定义struct本身包含struct VocabularyMetadataVec<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.

但是,我不明白,为什么我不能简单地从filtercollect方法中取回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:

  1. 不要遍历引用,而是遍历值本身.默认选择要求您拥有向量的所有权.使用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`?

    这篇关于如何在Rust中过滤自定义结构的向量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆