説明したのでメモ。
iter()
とinto_iter()
の違い
色々なiter()
とinto_iter()
について。公式リファレンスを見ればそれぞれの役割はわかります。
There are three common methods which can create iterators from a collection:
iter(), which iterates over &T.
iter_mut(), which iterates over &mut T.
into_iter(), which iterates over T.Various things in the standard library may implement one or more of the three, where appropriate.
少なくともstdライブラリではiter()
とiter_mut()
で得られるIteratorは&T
を、into_iter()
ではT
を持っている感じでやりましょう、という慣習があるらしいです(昔の議論とか見てると元々はそうではなかったっぽいですが)。
以下にVector
を使った例を示します。(Rust by Exampleより)
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
iter()
では参照が、into_iter()
では値が渡されているのがわかりますね。
into_iter()
とfor
ループの関係
また、この中でinto_iter()
はfor
ループで使われます。これもリファレンスから引用しますが、
let values = vec![1, 2, 3, 4, 5];
for x in values {
println!("{}", x);
}
これが、
let values = vec![1, 2, 3, 4, 5];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{}", x); };
},
};
result
}
これと同値らしいです。なので、for x in values
するとvalues
はmoveされてしまいます。
for x in values {
println!("{}", x);
}
let y = values; // move error
初心者がよくつまづくところですね。これを回避するには、
for x in &values {
println!("{}", x);
}
let y = values; // valid
こうやって、valuesの参照をとってからfor文に入れるんでしたね。何故こうするかは、into_iter()
がIntoIterator
トレイトで定義されていることがわかれば理解できます。Vector
がどうやってimplしているか見てみます。
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
IntoIterator
が&Vec
にもimplされています。こっちのinto_iter()
が呼ばれてたんですね。先ほどリファレンスの引用で、「into_iter()
はT
を返す」と書きましたが、このT
が何かの参照ではない保証はどこにもないので理解が適当だと注意する必要があります。
ちなみに、IntoIterator
はIteratorにもimplされているので、こういうことも可能です。
for x in values.iter() {
println!("{}", x);
}
into_iter()
の変な挙動
ここで、ちょっとした落とし穴について。配列に対してinto_iter()
を呼び出してみます。
let array = [1, 2, 3];
println!("2 in array: {}", array.into_iter().any(|x| x == 2));
コンパイルエラーになりました。
error[E0277]: the trait bound `&{integer}: std::cmp::PartialEq<{integer}>` is not satisfied
--> examples/1.rs:10:61
|
10 | println!("2 in array2: {}", array.into_iter().any(|x| x == 2));
| ^^ can't compare `&{integer}` with `{integer}`
|
= help: the trait `std::cmp::PartialEq<{integer}>` is not implemented for `&{integer}`
お?
参照が返ってきてます。まさか・・?
let array = [1, 2, 3];
println!("2 in array: {}", array.into_iter().any(|&x| x == 2));
|&x|
にしたら通りました。into_iter()
を読んだのに&T
が返ってきています。これはどういうことでしょうか。
Array
のリファレンスを見てみましょう。
Arrays of sizes from 0 to 32 (inclusive) implement the following traits if the element type allows it:
- ...
- IntoIterator (implemented for &[T; N] and &mut [T; N])
- ...
[T; N]
そのものはIntoIterator
をimplしてしませんが、&[T; N]
はしていると書いてありますね。これでわかりました。array.into_iter()
の例ではinto_iter()
が定義されていないため、自動で&array.into_iter()
を呼んでくれていたんですね!わかりづらい
ちなみに、for文ではこれは起こらないのでコンパイルは通りません。
for i in array {} // compile error
以上、ちょっとした落とし穴でした。
実装はされるか?
配列にIntoIterator
をimplしようという話は昔からありますが、まだ技術的に難しいようです。
-
impl Trait
がstableになるか、 -
const generics
が実装される必要があるとか。