如何在 Rust 中过滤自定义结构的向量?

Posted

技术标签:

【中文标题】如何在 Rust 中过滤自定义结构的向量?【英文标题】:How to filter a vector of custom structs in Rust? 【发布时间】:2017-11-23 13:03:19 【问题描述】:

我正在尝试过滤一个Vec<Vocabulary>,其中Vocabulary 是一个自定义struct,它本身包含一个struct VocabularyMetadata 和一个Vec<Word>

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

这用于处理 Web 应用程序中的路由,其中​​路由如下所示:/word/&lt;vocabulary_id&gt;/&lt;word_id&gt;

这是我当前尝试filter Vec&lt;Vocabulary&gt; 的代码:

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

这不起作用。我得到的错误是:

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

我不知道如何实现任何FromIterator,也不知道为什么需要这样做。在同一个网络应用程序的另一条路线中,我执行以下相同的文件,这很有效:

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 的元素。

我如何filter 我的Vec 并简单地获取条件为真的Vec&lt;Vocabulary&gt; 的元素?

【问题讨论】:

【参考方案1】:

学习如何创建minimal, reproducible example 是非常重要的编程技能。您的问题可以简化为:

struct Vocabulary;

fn main() 
    let numbers = vec![Vocabulary];
    let other_numbers: Vec<Vocabulary> = numbers.iter().collect();

让我们看看你的案例的错误信息:

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>`

这表示不能从 &amp;Vocabulary 的迭代器构建 Vec&lt;Vocabulary&gt;。你看得到差别吗?您有一个引用迭代器 (&amp;),不是 一个值迭代器。 Vec 怎么知道如何将您的引用转换为值?


你如何解决它?我不知道哪种方法最适合您的情况:

    不要迭代引用,迭代值本身。默认选择要求您拥有向量的所有权。使用into_iter 而不是iter

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

    如果你有一个可变引用,你也可以drain迭代器:

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

    通过克隆对象来复制对象。这要求您正在迭代的类型实现Clone。如果将此与过滤配对,则应在过滤后和调用collect() 之前调用cloned(),以避免克隆您丢弃的内容。

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

    不要收集值,收集 Vec 的引用。这要求您以后使用这些项目可以通过引用而不是按值获取项目:

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

请注意,我删除了多余的类型说明符(collect 上的 turbofish ::&lt;&gt;)。您只需要指定变量的类型或collect,而不是两者。事实上,所有三个示例都可以以let the_vocabulary: Vec&lt;_&gt; 开头,让编译器根据迭代器推断集合内的类型。这是惯用的样式,但出于演示目的,我保留了显式类型。


另见:

What is the difference between iter and into_iter? When should I use `drain` vs `into_iter`?

【讨论】:

我现在看到了小而重要的&amp;。完全忽略了这一点。我不明白使用对Vocabulary 的引用并只是在引用处给我对象的问题。它是关于对象的未知大小(结构,包含另一个 Vec 等)吗?对我来说,只有 (1) 第一个解决方案有效,因为 (2) std::clone::Clone 也不满意并且 (3) 仍然是不匹配的类型:app_structs::Vocabulary&amp;app_structs::Vocabulary。我会再次阅读into_iter。我读到它是破坏性的,实际上想要一个包含元素的副本,所以克隆可能是有意义的。 只是在引用处给我对象——你认为当前拥有该对象的事物会发生什么?如果您取得所有权,那么它将处于未定义状态,并且任何进一步使用它都会导致内存不安全。 嗯,这很奇怪。我想因为所有这些都发生在同一个范围内(好吧,也许我在“子范围”之间借用它,但它们无论如何都会返回所有权),我不需要担心这样的事情。编译器不能解决这个问题吗?我的意思是过滤器等毕竟是预定义的功能,它可以推理。你确定这不是关于未知大小而不是所有权吗? @Zelphir,编译器发现您想从提供&amp;Vocabulary 引用的迭代器中获取Vec&lt;Vocabulary&gt;filter 只是一个特征的常用方法。编译器只知道它的签名fn collect&lt;B&gt;(self) -&gt; B where B: FromIterator&lt;Self::Item&gt;,因此它检查类型Vec&lt;Vocabulary&gt; 是否实现了特征FromIterator&lt;&amp;Vocabulary&gt;,发现它没有并报告错误。 我是 rust 新手。你能解释一下或者给我一些资源吗|voc|我来自 JS,从来没有见过这样的东西

以上是关于如何在 Rust 中过滤自定义结构的向量?的主要内容,如果未能解决你的问题,请参考以下文章

23.Rust-集合(Collections)

你如何在 Rust 中定义自定义的 `Error` 类型?

如何在Rust中的函数堆栈上放置C结构?

迭代 Rust 中的结构向量

如何在Rust中修改向量的元素?

在 Rust 中迭代、过滤和映射数据从一个结构到另一个结构