RAII(Resource Acquisition Is Initialization)はC++から来たイディオムであり、リソースのライフサイクルがスタック内のオブジェクトのライフサイクルと厳密に関連しています。Rustでは、この概念がリソースの所有と解放のシステムの基礎となっており、従来のガベージコレクタ(GC)を必要としません。
多くの言語では、ガベージコレクタを使用してメモリとリソースを管理しており、これは定期的に不要なオブジェクトを「クリーンアップ」します。この戦略は遅延を増大させ、外部リソース(ファイル、ソケットなど)の即時解放を保証しません。低レベルおよびシステムプログラミングにおいて、この状況は容認できません:リソース管理には正確さと決定性が求められます。
Rustでは、各オブジェクトが自分のリソースを所有し、それを厳密に破棄される場所(スコープ外)で解放します。Drop(デストラクタの類似)を通じて実現されます。その結果、リソースは即座に解放され、暗黙の解放に関するエラーは最小限に抑えられます。Rustの型システムと所有権は、ほぼコンパイル時にリークや二重解放を防ぎます。
コード例:
struct FileWrapper { file: std::fs::File, } impl Drop for FileWrapper { fn drop(&mut self) { println!("FileWrapperがファイルを閉じます! (scope exit)"); } } fn main() { let _fw = FileWrapper { file: std::fs::File::create("test.txt").unwrap() }; // mainから出るときにdropが必ず呼び出されます }
主な特徴:
以前に移動(moved)された値に対してDropが呼び出されますか?
いいえ、値が移動された後、Dropは新しい所有者に対してのみ呼び出され、古いオブジェクトは「空」と見なされ、Dropはトリガーされません。
let file1 = FileWrapper {...}; let file2 = file1; // file1 move // Dropはfile2に対して一度だけ呼び出されます
スコープ内でpanic!やunwrap()が呼び出されるとdropの呼び出しは妨げられますか?
いいえ、パニックやエラーによる退出はデストラクタの呼び出しをキャンセルしません - スコープを外れたオブジェクトに対しては必ずDropが呼び出されます。
参照がオブジェクトを所有している場合、参照のライフサイクル終了時にdropは呼び出されますか?
いいえ、dropはオブジェクトの所有者に対してのみ呼び出され、参照は所有権を持ちません。ヒープリソースにはスマートポインタが必要です。
開発者がファイルのディスクリプタを参照として書き込み関数に渡しました。プログラムを終了した後、ファイルにはロックが残ったままでした。なぜならdropが呼び出されなかったからです(所有者がいなかったため、参照が使用された)。
利点:
欠点:
リソースの所有権は常にDropを実装した構造体に渡されました。すべてのオープンファイル、接続、またはロックはスコープの終了やpanic時に自動的に解放されます。複雑なプロジェクトにおいても安全で簡単にリソースを管理できます。
利点:
欠点: