借用チェッカーを満たすためのクローン
説明
借用チェッカーは、以下のいずれかを保証することで、Rustユーザーが安全でないコードを開発するのを防ぎます:可変参照が1つだけ存在するか、または複数の不変参照が存在するかのいずれかです。記述されたコードがこれらの条件を満たさない場合、開発者が変数をクローンすることでコンパイラエラーを解決しようとすると、このアンチパターンが発生します。
例
#![allow(unused)] fn main() { // define any variable let mut x = 5; // Borrow `x` -- but clone it first let y = &mut (x.clone()); // without the x.clone() two lines prior, this line would fail on compile as // x has been borrowed // thanks to x.clone(), x was never borrowed, and this line will run. println!("{x}"); // perform some action on the borrow to prevent rust from optimizing this //out of existence *y += 1; }
動機
特に初心者にとって、借用チェッカーの混乱する問題を解決するためにこのパターンを使用することは魅力的です。しかし、重大な結果があります。.clone()を使用すると、データのコピーが作成されます。2つの間の変更は同期されません – まるで2つの完全に別々の変数が存在するかのようです。
特殊なケースがあります – Rc<T>はクローンを賢く処理するように設計されています。これは、データのコピーを正確に1つだけ内部で管理します。Rcで.clone()を呼び出すと、ソースのRcと同じデータを指す新しいRcインスタンスが生成され、参照カウントが増加します。Rcのスレッドセーフな対応物であるArcにも同じことが当てはまります。
一般的に、クローンは意図的に行われるべきであり、その結果を完全に理解している必要があります。借用チェッカーのエラーを消すためにクローンが使用されている場合、それはこのアンチパターンが使用されている可能性がある良い兆候です。
.clone()がアンチパターンの兆候であっても、以下のような場合には非効率的なコードを書くことは問題ありません:
- 開発者が所有権にまだ慣れていない場合
- コードに厳しい速度やメモリの制約がない場合(ハッカソンプロジェクトやプロトタイプなど)
- 借用チェッカーを満たすことが本当に複雑で、パフォーマンスよりも可読性を優先したい場合
不要なクローンが疑われる場合、クローンが必要かどうかを評価する前に、Rust Bookの所有権に関する章を完全に理解する必要があります。
また、プロジェクトで常にcargo clippyを実行してください。これにより、.clone()が不要な一部のケースが検出されます。