ジェネリック関数に値を渡すと所有権がムーブされ2回使えない
ジェネリック型Tを所有権ごと受け取る関数に値を渡すと、Copyを実装していない型の場合は所有権がムーブされます。同じ値をもう一度渡そうとするとE0382エラーになります。
エラーメッセージの読み方
error[E0382]: use of moved value: `data`
E0382- エラーコード — `rustc --explain E0382` で詳しい説明が見られます
use of moved value: `data`- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 fn consume<T>(items: Vec<T>) -> usize { 2 items.len() 3 } 4 fn main() { 5 let data = vec![1, 2, 3]; 6 let a = consume(data ); ^ 7 let b = consume(data); 8 println!("{} {}", a, b); 9 }
consumeはVec<T>を所有権ごと受け取ります。dataを2回渡したいならcloneで複製する必要があります。
直し方: data を data.clone() にします。
パターン2
1 fn process<T>(items: Vec<T>) -> usize { 2 items.len() 3 } 4 fn main() { 5 let list = vec!["a", "b"]; 6 let a = process(list ); ^ 7 let b = process(list); 8 println!("{} {}", a, b); 9 }
ジェネリック関数でも、Copyを実装しない型を値渡しすれば通常の所有権ムーブと同じことが起こります。
直し方: list を list.clone() にします。
パターン3
1 fn handle<T>(items: Vec<T>) -> usize { 2 items.len() 3 } 4 fn main() { 5 let bag = vec![1.0, 2.0]; 6 let a = handle(bag ); ^ 7 let b = handle(bag); 8 println!("{} {}", a, b); 9 }
同じ値を複数の関数呼び出しで使い回したい場合、所有権を渡し切らないようcloneや参照渡しを検討します。
直し方: bag を bag.clone() にします。
パターン4
1 fn summarize<T>(items: Vec<T>) -> usize { 2 items.len() 3 } 4 fn main() { 5 let records = vec![10, 20, 30]; 6 let a = summarize(records ); ^ 7 let b = summarize(records); 8 println!("{} {}", a, b); 9 }
1回目のsummarize呼び出しでrecordsの所有権が消費され、2回目には渡す値が残っていません。
直し方: records を records.clone() にします。
パターン5
1 fn tally<T>(items: Vec<T>) -> usize { 2 items.len() 3 } 4 fn main() { 5 let entries = vec!["x", "y", "z"]; 6 let a = tally(entries ); ^ 7 let b = tally(entries); 8 println!("{} {}", a, b); 9 }
関数がVec<T>を借用(&Vec<T>)ではなく所有権ごと受け取る設計になっている点に注意が必要です。
直し方: entries を entries.clone() にします。
よくある誤解
「関数に渡すだけなら、値はコピーされて呼び出し元にも残るはず」という思い込みは誤りです。所有権を引数として受け取る関数(&Tではなく単なるT)に渡すと、Copyを実装しない型は消費され尽くします。
実務での勘所
ジェネリックな関数がTをCopyだと決め打ちできないのは、Tが実際にどんな型になるかを関数の定義時点では知りようがないためです。呼び出し側が渡すTが、ギガバイト単位のデータを持つVec<u8>や、複製すること自体に意味が無いFileハンドルのような型である可能性もあります。値の複製が常に安全・安価だとは限らない以上、Rustは既定で「複製できない(ムーブされる)」という保守的な前提を取り、本当に軽量な複製が可能な型に限ってCopyトレイトで明示的にオプトインさせています。