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

时间:2017-06-20 20:20:29

标签: vector struct rust filtering

我正在尝试过滤Vec<Vocabulary>,其中Vocabulary是自定义struct,其中包含struct VocabularyMetadataVec<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,也不知道为什么需要这样做。在同一个Web应用程序中的另一个路径中,我执行以下操作:

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

然而,我不明白,为什么我不能简单地从Vecfilter方法取回collect的元素。

我如何filter我的Vec并简单地获取条件为真的Vec<Vocabulary>元素?

1 个答案:

答案 0 :(得分:17)

非常重要的编程技巧,了解如何创建minimal, complete, verifiable example。您的问题可以简化为:

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

让我们看一下您案例的错误消息:

  
error[E0277]: the trait bound `std::vec::Vec<i32>: std::iter::FromIterator<&i32>` is not satisfied
 --> src/main.rs:3:50
  |
3 |     let other_numbers: Vec<i32> = numbers.iter().collect();
  |                                                  ^^^^^^^ a collection of type `std::vec::Vec<i32>` cannot be built from an iterator over elements of type `&i32`
  |
  = help: the trait `std::iter::FromIterator<&i32>` is not implemented for `std::vec::Vec<i32>`

这表示无法从Vec<Vocabulary>的迭代器构建&Vocabulary。你看得到差别吗?你有一个引用的迭代器(&),不是值的迭代器。 Vec如何知道如何将引用转换为值?

你是如何解决的?我不知道在你的情况下哪种方法最有效:

  1. 不要遍历引用,迭代值本身。这要求您拥有向量的所有权。使用into_iter代替iter

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .into_iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    
  2. 通过克隆对象来复制对象。这要求您在迭代的类型实现Clone。如果您将此与过滤配对,则应在过滤后调用cloned(),然后再调用collect()以避免克隆您丢弃的内容。

    let the_vocabulary: Vec<Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .cloned()
        .collect();
    
  3. 不要收集价值,收集Vec个参考文献。这要求您使用之后的项目可以通过引用而不是值来获取项目:

    let the_vocabulary: Vec<&Vocabulary> = vocabulary_context
        .vocabularies
        .iter()
        .filter(|voc| voc.metadata.identifier == vocabulary_id)
        .collect();
    
  4. 请注意,我删除了冗余类型说明符(::<>上的turbofish collect)。您只需要指定变量的类型或collect,而不是两者。实际上,所有三个示例都可以从let the_vocabulary: Vec<_>开始,让编译器根据迭代器推断集合中的类型。这是惯用的风格,但为了演示目的,我保留了明确的类型。

    另见: