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
- メディア: ペーパーバック
- この商品を含むブログを見る