ドロップ
Drop トレイトには drop というメソッドが 1 つだけあります。このメソッドは、オブジェクトがスコープを抜けるときに自動的に呼び出されます。Drop トレイトの主な用途は、実装者のインスタンスが所有しているリソースを解放することです。
Box、Vec、String、File、Process は、リソースを解放するために Drop トレイトを実装している型の例です。Drop トレイトは、任意のカスタムデータ型に対して手動で実装することもできます。
次の例では、drop 関数が呼び出されたときにそれを知らせるため、コンソールへの出力を追加しています。
struct Droppable { name: &'static str, } // この自明な `drop` の実装は、コンソールへの出力を追加します。 impl Drop for Droppable { fn drop(&mut self) { println!("> Dropping {}", self.name); } } fn main() { let _a = Droppable { name: "a" }; // ブロック A { let _b = Droppable { name: "b" }; // ブロック B { let _c = Droppable { name: "c" }; let _d = Droppable { name: "d" }; println!("Exiting block B"); } println!("Just exited block B"); println!("Exiting block A"); } println!("Just exited block A"); // 変数は `drop` 関数を使って手動でドロップできます drop(_a); // TODO ^ この行をコメントアウトしてみてください println!("end of the main function"); // `_a` はすでに(手動で)`drop` されているため、ここで再度 `drop` されることは*ありません* }
より実用的な例として、不要になった一時ファイルを自動的にクリーンアップするために Drop トレイトを使用する方法を示します。
use std::fs::File; use std::path::PathBuf; struct TempFile { file: File, path: PathBuf, } impl TempFile { fn new(path: PathBuf) -> std::io::Result<Self> { // 注意: File::create() は既存のファイルを上書きします let file = File::create(&path)?; Ok(Self { file, path }) } } // TempFile がドロップされるとき: // 1. まず、カスタムの drop 実装が実行されます。この時点ではファイルはまだ開いていますが、 // パスを使ってファイルシステムから削除できます。 // 2. 次に、drop から戻った後、Rust は各フィールドを自動的にドロップするため、 // File の drop が実行され、ファイルハンドルが閉じられます。 impl Drop for TempFile { fn drop(&mut self) { // 注意: ここでは File はまだ開いています — フィールドのデストラクタはこのメソッドの後に実行されます。 if let Err(e) = std::fs::remove_file(&self.path) { eprintln!("Failed to remove temporary file: {}", e); } println!("> Dropped temporary file: {:?}", self.path); // このメソッドが戻った後、Rust は各フィールド(`file` を含む)をドロップし、 // それによって基礎となるファイルハンドルが閉じられます。 } } fn main() -> std::io::Result<()> { // drop の動作を示すために新しいスコープを作成します { let temp = TempFile::new("test.txt".into())?; println!("Temporary file created"); // temp がスコープを抜けると、ファイルは自動的にクリーンアップされます } println!("End of scope - file should be cleaned up"); // 必要であれば手動でドロップすることもできます let temp2 = TempFile::new("another_test.txt".into())?; drop(temp2); // ファイルを明示的にドロップします println!("Manually dropped file"); Ok(()) }