VectorをIteratorに変換する時にいつも混乱していたので整理した。
混乱
あるVectorの要素すべてを3倍するコードを考える。
fn main() { let vec1 = vec![1,2,3,4,5]; let vec2 = vec1.iter() .map(|i| i * 3) .collect::<Vec<i32>>(); println!("{:?}", vec1); println!("{:?}", vec2); }
このコードはコンパイルできるが、以下のような疑問がある。
- 5行目で
vec1はなぜ使えるのか? 3行目のvec1.iter()で使われているじゃないか! map(|i| i * 3)のiは参照なのか値なのか?
これらの疑問に関する答えは、
iter()はVectorをmoveしない。into_iter()はVectorをmoveする。iter()はVectorから「参照のコレクションであるIterator」を作成し、into_iter()はVectorから「値のコレクションであるIterator」を作成する。
ということになる。
into_iter()の挙動
呼び出し元のVectorの所有権
fn main() { let vec1 = vec![1,2,3,4,5]; let vec2 = vec1.into_iter() .map(|i| i * 3) .collect::<Vec<i32>>(); println!("{:?}", vec1); // Error! }
このコードはコンパイルできない。というのも、3行目の into_iter()はvec1をmoveするからである。
このように、 into_iter()は、呼び出し元のVectorをmoveする。
mapに渡される要素は参照なのか?
fn main() { let vec1 = vec![1,2,3,4,5]; let vec2 = vec1.into_iter() .map(|i| *i * 3) // Error! .collect::<Vec<i32>>(); }
このコードはコンパイルできない。というのも、4行目の map(|i| *i * 3)の iは、参照ではなく値だからである。
このように、into_iter()に連なる mapには、参照ではなく値が渡される。
iter()の挙動
呼び出し元のVectorの所有権
fn main() { let vec1 = vec![1,2,3,4,5]; let vec2 = vec1.iter() .map(|i| i * 3) .collect::<Vec<i32>>(); println!("{:?}", vec1); // OK! }
このコードはコンパイルできる。iter()はvec1をmoveしないからである。
このように、 iter()は、呼び出し元のVectorをmoveしない。
mapに渡される要素は参照なのか?
fn main() { let vec1 = vec![1,2,3,4,5]; let vec2 = vec1.iter() .map(|i| *i * 3) // OK! .collect::<Vec<i32>>(); }
このコードはコンパイルできる。4行目の map(|i| *i * 3)の iは、値ではなく参照だからである。
このように、iter()に連なる mapには、参照が渡される。
結論
VectorをIteratorに変換して処理を行いたいとき、
- そのVectorを後で利用するなら
iter()を使う。その場合、mapには要素の参照が渡される。 - そのVectorを後で利用しないなら
into_iter()を使う。その場合、mapには要素の値が渡される。
速度について
一応簡単にベンチマークして、iter()とinto_iter()の間に速度的な差があるのか検証した。
なんとなくiter()の方が遅いような気がするが、誤差幅を考えるとほとんど差がないように見える。
ベンチマークのコード:
#![feature(test)] extern crate test; pub fn iter(vec: Vec<i64>) { vec.iter().map(|i| i * 2).sum::<i64>(); } pub fn into_iter(vec: Vec<i64>) { vec.into_iter().map(|i| i * 2).sum::<i64>(); } #[cfg(test)] mod tests { use super::*; use test::Bencher; #[bench] fn bench_iter(b: &mut Bencher) { let range = 0..100000; let vec = range.collect::<Vec<i64>>(); b.iter(|| iter(vec.clone())); } #[bench] fn bench_into_iter(b: &mut Bencher) { let range = 0..100000; let vec = range.collect::<Vec<i64>>(); b.iter(|| into_iter(vec.clone())); } }
結果:
> cargo bench
Compiling iterator-bench v0.1.0
Finished release [optimized] target(s) in 0.61 secs
Running target/release/deps/iterator_bench-b01db65774dc9996
running 2 tests
test tests::bench_into_iter ... bench: 38,553 ns/iter (+/- 14,812)
test tests::bench_iter ... bench: 39,507 ns/iter (+/- 20,207)
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out
Programming Rust: Fast, Safe Systems Development
- 作者: Jim Blandy,Jason Orendorff
- 出版社/メーカー: O'Reilly Media
- 発売日: 2017/12/21
- メディア: ペーパーバック
- この商品を含むブログを見る