iterとinto_iterの違いは何ですか?


174

私はこのコードスニペットを含むRust by Exampleチュートリアルをやっています:

// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];

// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter()     .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));

// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];

// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter()     .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));

私は完全に混乱してVecいますiter。yield参照から返されたイテレータとinto_iteryields値から返されたイテレータの場合、配列の場合、これらのイテレータは同じですか?

これら2つの方法のユースケース/ APIは何ですか?

回答:


146

TL; DR:

  • によって返されるイテレータは、コンテキストに応じて、またはのinto_iterいずれかを生成します。T&T&mut T
  • によって返される反復子は、慣例により、iterを生成し&Tます。
  • によって返される反復子は、慣例により、iter_mutを生成し&mut Tます。

最初の質問は「何into_iterですか?」です。

into_iterIntoIterator特徴から来る:

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

特定の型をイテレータに変換する方法を指定する場合は、この特性を実装します。最も注目に値するのは、型が実装IntoIteratorする場合、forループで使用できることです。

たとえば、...を3回Vec実装しIntoIteratorます!

impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>

各バリアントは少し異なります。

これはを消費しVec、その反復子値をT直接)生成します

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}

他の2つは参照によってベクトルを取得し(どちらの場合も参照であるinto_iter(self)ため、のシグネチャに惑わされないでくださいself)、それらのイテレータは内の要素への参照を生成しますVec

これ不変の参照を生成します

impl<'a, T> IntoIterator for &'a Vec<T> {
    type Item = &'a T;
    type IntoIter = slice::Iter<'a, T>;

    fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}

これ変更可能な参照を生成しますが

impl<'a, T> IntoIterator for &'a mut Vec<T> {
    type Item = &'a mut T;
    type IntoIter = slice::IterMut<'a, T>;

    fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}

そう:

違いは何であるiterとはinto_iter

into_iterイテレータが値を生成するかどうか、イテレータを取得する一般的な方法です。このイテレータが不変の参照であるか、変更可能な参照であるかは、コンテキストに依存し、時々驚くかもしれません。

iterそしてiter_mutアドホックな方法です。したがって、それらの戻り値の型はコンテキストに依存せず、従来、それぞれ不変の参照と可変の参照を生成する反復子になります。

Rust by Exampleの投稿者は、into_iter呼び出されるコンテキスト(つまり、タイプ)への依存から生じる驚きを説明し、次の事実を使用して問題をさらに複雑化しています。

  1. IntoIterator以下のために実装されていない[T; N]だけのために、&[T; N]&mut [T; N]
  2. メソッドが値に対して実装されていない場合、代わりにその値への参照が自動的に検索されます

これはinto_iter、すべてのタイプ(を除く[T; N])が3つのバリエーションすべて(値と参照)に対して実装するため、非常に驚くべきことです。配列は、項目を放棄するために「縮小」できないため、値を生成する反復子を実装することはできません。

なぜ配列がIntoIterator(そのような驚くべき方法で)実装されるのかについては、forループ内での配列への参照を反復処理できるようにするためです。


14
私はこのブログの記事が役に立ったと評価:hermanradtke.com/2015/06/22/...
POY

>このイテレータが値を生成するかどうか、不変の参照または変更可能な参照はコンテキストに依存するかどういう意味で、どのように対処するのですか?たとえば、iter_mutに変更可能な値を強制する方法を教えてください。
Dan M.

@DanM .:(1)これはinto_iter、レシーバが値、参照、または可変参照であるかどうかに基づいて実装を選択することを意味します。(2)Rustには変更可能な値はありません。つまり、所有権があるため、どの値も変更可能です。
Matthieu M.

@ MatthieuM.hm、私のテストではそうではないようです。私はIntoIterを実装し&'a MyStruct&mut 'a MyStructおり、ラムダで引数を使用しinto_iter().for_each()mut値を呼び出した場合でも、存在する場合は常に最初のものが選択され&mutました。
Dan M.

1
@Ixx:ありがとうございます。真ん中に答えが埋もれないように、質問の先頭にTL; DRを提供することにしましたが、どう思いますか?
Matthieu M.

77

私(Rustの初心者)は、他の回答では提供されなかった簡単な回答を求めてGoogleからここに来ました。これが簡単な答えです。

  • iter() 参照によって項目を反復します
  • into_iter() アイテムを反復処理し、新しいスコープに移動します
  • iter_mut() アイテムを反復処理し、各アイテムへの変更可能な参照を提供します

つまりfor x in my_vec { ... }、本質的には、スコープ内のmy_vec.into_iter().for_each(|x| ... )両方moveの要素に相当します。my_vec...

データを「確認」するだけの場合はを使用しiter、データを編集または変更iter_mutする必要がある場合はを使用し、新しい所有者を与える必要がある場合はを使用しますinto_iter

これは役に立ちました:http : //hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html

これをコミュニティーwikiにして、Rustプロが間違いを犯した場合にこの回答を編集できるようにしています。


7
ありがとう...受け入れられた回答がiterとの違いを明確に表す方法を理解することは困難into_iterです。
mmw

それがまさに私が探していたものです!
Cyrusmith

6

.into_iter()配列自体には実装されていませんが、のみ実装されてい&[]ます。比較:

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T

impl<T> IntoIterator for Vec<T>
    type Item = T

以来IntoIteratorのみに定義され&[T]、スライス自体は同じ方法ドロップすることはできませんVecあなたが値を使用するときに。(値は移動できません)

さて、なぜそうなのかは別問題なので、自分で学びたいです。推測:配列はデータ自体であり、スライスはそれに対するビューにすぎません。実際には、配列を値として別の関数に移動することはできません。配列のビューを渡すだけなので、そこで配列を使用することはできません。


IntoIteratorはにも実装されているため&'a mut [T]、オブジェクトを配列の外に移動できます。これは、return struct IntoIter<T>が生涯引数をIter<'a, T>持たない一方で、前者がスライスを保持できないという事実に関連していると思います。
rodrigo 2016年

mut値を変更できることを意味します。移動することはできません。
viraptor 2016年

@rodrigo let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });=>「エラー:借用したコンテンツから移動できません」
viraptor

ええ、私はあなたが正しいと思い、配列から値を移動することはできません。ただし、ArrayIntoIterライブラリの一部として、安全でないRustを使用して一種の構造体を実装することは可能だと私はまだ考えています... Vecとにかくそれらのケースに使用する必要があるので、価値がないかもしれません。
rodrigo 2016年

だから私は理解できません... 自動的に変換するために魔法を使っているので、それがarray.into_iter戻る理由です-そしてそうなら、値の移動または値の移動に何が関係しているのか理解できません。それとも、@ rodrigoが言ったように、(何らかの理由で)配列から値を移動できないために参照を取得したのですか?まだ非常に混乱しています。&T&array.into_iter
2016年

2

もう少しはっきりさせたいことがあると思います。コレクションなど、種類、Vec<T>およびVecDeque<T>、持っinto_iter利回りその方法をT、彼らが実装しているためIntoIterator<Item=T>Foo<T>反復されるタイプを作成するのを止めるものは何もありません。それが生成されない場合T、別のタイプが生成されUます。つまり、をFoo<T>実装しIntoIterator<Item=U>ます。

実際には、いくつかの例がありますstd&Path implements IntoIterator<Item=&OsStr>&UnixListener implements IntoIterator<Item=Result<UnixStream>>


違いinto_iteriter

into_iterとの違いに関する元の質問に戻りiterます。他の人が指摘したのと同様に、違いは、into_iterIntoIterator指定された任意のタイプを生成できる必須メソッドであることIntoIterator::Itemです。典型的には、型実装する場合はIntoIterator<Item=I>、慣例により、また、2つのアドホック・メソッドがありますiterし、iter_mutその収率&I&mut Iそれぞれ。

暗黙into_iterのトレイトを使用して、メソッドを持つ型(つまり、反復可能)を受け取る関数を作成できるということです。

fn process_iterable<I: IntoIterator>(iterable: I) {
    for item in iterable {
        // ...
    }
}

しかし、我々はできません*持っているタイプを必要とするためにバインドされた形質使うiter方法やiter_mut、彼らはただの慣習だから、方法を。またはinto_iterよりも広く使用できます。iteriter_mut

iterおよびの代替iter_mut

観察するもう1つの興味深い点iterは、それが生成するイテレータを取得する唯一の方法ではないということです&T。慣例によりSomeCollection<T>stdここiterでも、メソッドを持つコレクション型には不変の参照型が&SomeCollection<T>実装されていIntoIterator<Item=&T>ます。たとえば、&Vec<T> implements IntoIterator<Item=&T>なので、繰り返し処理できます&Vec<T>

let v = vec![1, 2];

// Below is equivalent to: `for item in v.iter() {`
for item in &v {
    println!("{}", item);
}

両方v.iter()がと同じである場合、なぜRustは両方を提供するのですか?人間工学用です。でループ、それは使用に簡潔もう少しだより。しかし、他の場合では、よりも明確です:&vIntoIterator<Item=&T>for&vv.iter()v.iter()(&v).into_iter()

let v = vec![1, 2];

let a: Vec<i32> = v.iter().map(|x| x * x).collect();
// Although above and below are equivalent, above is a lot clearer than below.
let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();

同様に、forループでv.iter_mut()は、と置き換えることができます&mut v

let mut v = vec![1, 2];

// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
    *item *= 2;
}

型に(実装)into_iteriterメソッドを提供するタイミング

型が繰り返される「ウェイ」が1つしかない場合は、両方を実装する必要があります。ただし、2つ以上の方法で繰り返すことができる場合は、代わりにそれぞれの方法にアドホックメソッドを提供する必要があります。

たとえば、Stringは、を提供することinto_iterも提供することもしないiterため、反復する2つの方法があります。その表現をバイト単位で反復するか、文字単位で反復するかです。代わりに、メソッドの代わりに、bytesバイトcharsを反復するためと文字を反復するための2つのメソッドを提供しますiter


*そうですね、技術的には、特性を作成することでそれを行うことができます。ただし、impl使用するタイプごとにその特性を設定する必要があります。その間、多くのタイプがstdすでに実装していますIntoIterator

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.