Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ドロップ

Drop トレイトには drop というメソッドが 1 つだけあります。このメソッドは、オブジェクトがスコープを抜けるときに自動的に呼び出されます。Drop トレイトの主な用途は、実装者のインスタンスが所有しているリソースを解放することです。

BoxVecStringFileProcess は、リソースを解放するために 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(())
}