web-dev-qa-db-ja.com

Rustでカスタム構造体のベクトルをフィルタリングする方法は?

私はVec<Vocabulary>をフィルタリングしようとしています。ここで、Vocabularyはカスタムstructであり、それ自体にstructVocabularyMetadataVec<Word>が含まれています。

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

これはWebアプリケーションでルートを処理するためのもので、ルートは次のようになります:/Word/<vocabulary_id>/<Word_id>

Vec<Vocabulary>filterしようとしている現在のコードは次のとおりです。

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

したがって、StringFromIteratorを実装しているようです。

ただし、取得できません。Vecまたはfilterメソッドからcollectの要素を単純に取得できないのはなぜですか。

filter my Vecを使用して、条件がtrueであるVec<Vocabulary>の要素を取得するにはどうすればよいですか?

13

最小限の再現可能な例 の作成方法を学ぶことは、非常に重要なプログラミングスキルです。あなたの問題はこれに減らすことができます:

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

これは、_Vec<Vocabulary>_を_&Vocabulary_のイテレータから構築できないことを示しています。違いがわかりますか?参照のイテレータ(_&_)、not値のイテレータがあります。参照を値に変換する方法をVecはどのように知っていますか?


どのように修正しますか?私はあなたの状況で何が最もうまくいくかわかりません:

  1. 参照を反復するのではなく、値自体を反復します。デフォルトの選択では、ベクターの所有権が必要です。 iterの代わりに_into_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();
    _
  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();
    _

冗長な型指定子(collectのturbofish _::<>_)を削除したことに注意してください。変数のタイプまたはcollectの両方を指定する必要はありません。実際、3つの例はすべて_let the_vocabulary: Vec<_>_で始まり、コンパイラーがイテレーターに基づいてコレクション内の型を推測できるようにすることができます。これは慣用的なスタイルですが、デモのために明示的なタイプを保持しました。


以下も参照してください。

19
Shepmaster