如何在 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/<vocabulary_id>/<word_id>
。
这是我当前尝试filter
Vec<Vocabulary>
的代码:
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
。
但是,我不明白,为什么我不能简单地从 filter
或 collect
方法中取回 Vec
的元素。
我如何filter
我的Vec
并简单地获取条件为真的Vec<Vocabulary>
的元素?
【问题讨论】:
【参考方案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>`
这表示不能从 &Vocabulary
的迭代器构建 Vec<Vocabulary>
。你看得到差别吗?您有一个引用迭代器 (&
),不是 一个值迭代器。 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 ::<>
)。您只需要指定变量的类型或collect
,而不是两者。事实上,所有三个示例都可以以let the_vocabulary: Vec<_>
开头,让编译器根据迭代器推断集合内的类型。这是惯用的样式,但出于演示目的,我保留了显式类型。
另见:
What is the difference between iter and into_iter? When should I use `drain` vs `into_iter`?【讨论】:
我现在看到了小而重要的&
。完全忽略了这一点。我不明白使用对Vocabulary
的引用并只是在引用处给我对象的问题。它是关于对象的未知大小(结构,包含另一个 Vec 等)吗?对我来说,只有 (1) 第一个解决方案有效,因为 (2) std::clone::Clone
也不满意并且 (3) 仍然是不匹配的类型:app_structs::Vocabulary
与 &app_structs::Vocabulary
。我会再次阅读into_iter
。我读到它是破坏性的,实际上想要一个包含元素的副本,所以克隆可能是有意义的。
只是在引用处给我对象——你认为当前拥有该对象的事物会发生什么?如果您取得所有权,那么它将处于未定义状态,并且任何进一步使用它都会导致内存不安全。
嗯,这很奇怪。我想因为所有这些都发生在同一个范围内(好吧,也许我在“子范围”之间借用它,但它们无论如何都会返回所有权),我不需要担心这样的事情。编译器不能解决这个问题吗?我的意思是过滤器等毕竟是预定义的功能,它可以推理。你确定这不是关于未知大小而不是所有权吗?
@Zelphir,编译器发现您想从提供&Vocabulary
引用的迭代器中获取Vec<Vocabulary>
。 filter
只是一个特征的常用方法。编译器只知道它的签名fn collect<B>(self) -> B where B: FromIterator<Self::Item>
,因此它检查类型Vec<Vocabulary>
是否实现了特征FromIterator<&Vocabulary>
,发现它没有并报告错误。
我是 rust 新手。你能解释一下或者给我一些资源吗|voc|
我来自 JS,从来没有见过这样的东西以上是关于如何在 Rust 中过滤自定义结构的向量?的主要内容,如果未能解决你的问题,请参考以下文章