前回からイテレータを勉強してます。前回は残念なことに直なメソッドばかりで紛糾?しなかったです。今回から紛糾しそうなメソッドに入っていきたいと思います。その入口は count() とな。実行すると結果を返してくれるけれどイテレータを「消費」してしまうメソッドです。そのままでは意味ないけれど2度数えることはできないの?<訂正あり>
※2022年11月28日訂正: 1.10をstd::iterのごとくに使用してますが、1..10はstd::ops::Range です。共通するお名前で同様な機能のTraitsが実装されているのでイテレータではないのにイテレータとして練習してしまいました。以下コード中 1..10の代わりに[1, 2, 3, 4, 5, 6, 7, 8, 9].iter()とすれば「正式な」イテレータになるんでないの、と思われます。
※「やっつけな日常」投稿順 Index はこちら
参照させていただいておりますRustのドキュメントは以下です。
案の定なのだけれども、イテレータにもRustの「安全」なメモリ管理、所有権とかが登場してくるのね。忘却力の年寄はうっかりミス多数だけれども、コンパイラが後ろで監視してくれていると思えば安心?エラーのご指摘煩いけれども。。。
今回実験用のソースコード全文
今回実験は、イテレータを「消費」してしまうcount()メソッドで「2回数える」という実用にはならなそうなコードです。列挙するとこんな感じ。よく分からんな。
- rangeオブジェクトに対して2回目作用させてエラーになる
- rangeオブジェクトをby_refしておいて1回目、元のオブジェクトで2回目数える
- rangeオブジェクトをcloneしておいて2回目数える
- 配列をイテレータ化してclonedしてcollectでVec型にして、元の配列とVecのそれぞれで数えて合計2回
- 配列をイテレータ化してcopiedしてcollectでVec型にして、元の配列とVecのそれぞれで数えて合計2回
全文が以下に
fn main() { //count again let tgt = 1..10; println!("count0_1: {}", tgt.count()); // println!("count2: {}", tgt.count()); <== ERROR! //by_ref let mut tgt1 = 1..10; let tgt1_clone = tgt1.clone(); println!("count1_1.by_ref: {}", tgt1.by_ref().count()); println!("count1_2: {}", tgt1.count()); println!("count1_3 clone: {}", tgt1_clone.count()); //cloned.collect() let tgt_a = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut tgt_avec : Vec<_> = tgt_a.iter().cloned().collect(); tgt_avec[1]=999; println!("count2_1.cloned: {}", tgt_avec[1]); println!("count2_1 count : {}", tgt_avec.iter().count()); println!("count2_2.org : {}", tgt_a[1]); println!("count2_2 count : {}", tgt_a.iter().count()); //copied.collect() let tgt_b = [1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut tgt_bvec : Vec<_> = tgt_b.iter().copied().collect(); tgt_bvec[1]=999; println!("count3_1.copied: {}", tgt_bvec[1]); println!("count3_1 count : {}", tgt_bvec.iter().count()); println!("count3_2.org : {}", tgt_b[1]); println!("count3_2 count : {}", tgt_b.iter().count()); }
rangeオブジェクトにcount()2回目
2回目がエラーになるのは以下のようなコードです。
エラーの原因は消費されるからではなく、最初のcount()で所有権がmoveしてしまうからみたいです。それにしてもどこへ行ってしまうのだろう?
rangeオブジェクトをcloneまたはby_ref
rangeオブジェクトをcloneしておけば、元のオブジェクトに何があろうと処理できるハズ。一方、イテレータには by_ref() という「借用」メソッドもあるので、借用も試みてます。なお借用はmutでないとダメだと。
借用の結果、元のオブジェクトが move 済だといって怒られることは無くなったです。しかし借用先のcount()で「消費」されてしまうので戻った後再度countしても結果は0。エラーは出ないし、本来そうあるべきな1本筋の通った挙動ではあるのですが、今回の2回数えるという目的とは違うです。
一方元のオブジェクトをクローンしておきゃ、大丈夫だあ。まあ別なものを数えなおしているだけなので当たり前か。
イテレータのclonedメソッドを使ってみる
いままでイテレータといいつつ range オブジェクトに作用させてきましたが、clonedメソッドに関しては「素の」数字の羅列であるrangeオブジェクトは扱えないようです。ホントか?
そこで配列を作り、その配列をiter()でイテレータ化し、cloned、さらにcollect()でVec型に直すということをしてしまいました。配列とその要素をクローンしたVec型が共存する感じですかね。mutで宣言したVec型の要素を一部書き換えても元の配列はびくともしません。当たり前か。
そしてどちらもiter().count()で要素数を数えられます。
これは2回目数えているというより、別々のものを1回づつ数えているってことだね。2回目じゃないじゃん。
イテレータのcopiedメソッドを使ってみる
clonedメソッドに似たもので、copiedというメソッドもあるので実験してみました。
これでは違いがまったく分からんな。
身に染みて違いを知るにはどうしたらよかでしょうか?また今度だな。