問題の歴史を見てみましょう:
Rustでは、メモリ管理と所有の概念は、オブジェクトがどのように移動し、コピーされるかを明確に定義することを要求します。言語の初期においては、バイトを単純にコピーすることと(割り当てやロジックなし)、深いクローン(例えば、文字列やベクターのコピー)を区別することが重要でした。これを実現するために、2つのトレイト— CopyとCloneが導入されました。
問題は、すべてのデータ型が同じように安価にコピーできるわけではないことです。一部の構造体にとっては、コピーは単なるビットのコピーですが(例えば、整数やCopy型のタプル)、他のものにとっては(例えば、StringやVec)、メモリの割り当てに追加の作業が伴います。CopyとCloneの分離は、無効なコピーを試みた場合にRustがコンパイルエラーを出すことを可能にします。
解決策:
Copyとしてマークされた型は、渡されたり、代入されたり、関数に渡されると自動的にコピーされます。これらの型のオブジェクトは、コピー後も有効なままです。.clone()メソッド呼び出しを想定するCloneを実装します。コード例:
#[derive(Debug, Copy, Clone)] struct Point { x: i32, y: i32, } fn main() { let p1 = Point { x: 1, y: 2 }; let p2 = p1; // p1は「無効」にはならない println!("{:?} {:?}", p1, p2); }
重要な特徴:
Copy - 自動的なビット単位のコピーで、手動で呼び出す必要はなく、所有権には影響しません。Clone - 明示的な深いコピーで、heapデータを含む構造体に適しています。heapに割り当てられたデータを持つ型はCopyを持つことができますか?
いいえ、heapデータを含む型(例えば、StringやVec)は自動的にCopyを実装できません。なぜなら、これによりメモリの二重解放が発生するからです。
型がCopyを実装している場合、異なるロジックでCloneも手動で実装できますか?
はい、Cloneは手動で実装でき、ロジックは異なっても構いませんが、CopyとCloneは一貫性があるべきと推奨されます:Copyは単にCloneを呼び出すだけで追加の割り当てを行いません。
#[derive(Copy)] struct X; impl Clone for X { fn clone(&self) -> X { *self } }
構造体がCopyフィールドのみを含んでいるが、#[derive(Copy)]としてマークされていない場合、それはCopyになりますか?
いいえ、型はその構成によって自動的にCopyになりません。型のためには明示的なCopyの実装が必要です。
heapデータを持つ型が誤ってCopyとしてマークされ、ファイナライズ中にメモリの二重解放が発生します。
利点:
欠点:
軽量構造体(座標、色)にはCopyを、複雑な構造体(文字列、ベクター)にはCloneを使用します。コードは安全で予測可能です。
利点:
欠点: