如何过滤自定义结构体的向量?

52
我试图过滤一个包含自定义结构体VocabularyVec<Vocabulary>,其中Vocabulary包含一个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

然而,我不明白为什么我不能从filtercollect方法中简单地获取Vec的元素。

我如何过滤我的Vec并简单地获取满足条件的Vec<Vocabulary>元素?

1个回答

116

学会创建一个最小化且可复现的示例对于编程技能来说非常重要。你的问题可以缩小到以下这个范围:

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 怎么知道如何将你的引用转换为值呢?
如何修复它?我不知道什么最适合你的情况:
  1. 不要遍历引用,而是直接遍历值本身。默认选项要求您拥有向量的所有权。使用 into_iter 而不是 iter

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

    如果您拥有可变引用,也可以通过 消耗 迭代器来完成操作:

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .drain(..)
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    
  2. 通过克隆复制对象。这需要你正在迭代的类型实现了Clone。如果您将其与过滤配对使用,应在过滤后调用cloned()然后再调用collect(),以避免在丢弃某些内容时进行克隆。

  3. let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .cloned()
        .collect();
    
    不要收集值,而是收集引用的 Vec。这需要在使用这些项目之后,可以通过引用而不是值来获取项目:
  4. let the_vocabulary: Vec<&Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    
    请注意,我删除了冗余的类型指示符(在collect上的turbofish ::<>)。您只需要在变量或collect上指定类型,而不是两者兼备。实际上,所有三个示例都可以以let the_vocabulary: Vec<_>开始,以使编译器根据迭代器推断出集合内部的类型。这是惯用的样式,但出于演示目的,我保留了显式类型。

    另请参见:


1
我现在明白了那个小但重要的&。完全忽略了它。我不明白使用对Vocabulary的引用并只给我引用处的对象有什么问题。是关于对象(结构体,包含另一个Vec等)大小未知的问题吗?对我来说,只有(1)第一种解决方案可行,因为(2)std::clone::Clone也无法满足,而(3)仍然是不匹配的类型:app_structs::Vocabulary&app_structs::Vocabulary。我会再次阅读into_iter。我读到它是具有破坏性的,实际上想要所包含元素的副本,因此克隆可能是有意义的。 - Zelphir Kaltstahl
“只是给我引用的对象” - 你认为当前拥有该对象的东西会发生什么?如果你取得所有权,那么它将处于未定义状态,任何进一步使用都会导致内存不安全。 - Shepmaster
嗯,这很奇怪。我以为因为所有这些都发生在同一个作用域中(好吧,也许我在“子作用域”之间借用它们,但它们归还所有权),所以我不需要担心这样的事情。编译器不能找出来吗?我的意思是,过滤器等都是预定义函数,它可以理解的。你确定这不是关于未知大小而不是所有权吗? - Zelphir Kaltstahl
1
@Zelphir,编译器已经确定您想从提供&Vocabulary引用的迭代器中获取Vec<Vocabulary>filter只是一个trait的普通方法。编译器所知道的仅仅是它的签名fn collect<B>(self) -> B where B: FromIterator<Self::Item>,因此它检查类型Vec<Vocabulary>是否实现了traitFromIterator<&Vocabulary>,发现它没有,并报告错误。 - red75prime
1
我是 Rust 的新手。你能解释一下或者给我一些资源,来说明 |voc| 是什么意思吗?我来自 JS,从未见过这样的东西。 - bill.gates
1
@Ifaruki https://doc.rust-lang.org/book/ch13-01-closures.html - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接