スコープガード

スコープガードは Drop トレイトを使って、スコープを抜けるときに、アンワインド中であっても自動的にクリーンアップコードを実行します。

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

use scopeguard::{ScopeGuard, guard};
use std::fs::{self, File};
use std::io::Write;

fn download_successful() -> bool {
    // [...]
    true
}

fn main() {
    let path = "download.tmp";
    let mut file = File::create(path).expect("一時ファイルを作成できません");

    // ファイル作成の直後にクリーンアップを設定する
    let cleanup = guard(path, |path| {
        println!("ダウンロードに失敗したため削除します: {:?}", path);
        let _ = fs::remove_file(path);
    });

    writeln!(file, "partial data...").unwrap();

    if download_successful() {
        // ダウンロードに成功したら、ファイルを残す
        let path = ScopeGuard::into_inner(cleanup);
        println!("ダウンロード '{path}' が完了しました!");
    }
    // それ以外の場合は、ガードが実行されてファイルを削除する
}
  • この例はダウンロードのワークフローをモデル化したものです。最初に一時ファイルを作成し、その後、ダウンロードが失敗した場合にそのファイルが削除されるようスコープガードを使います。

  • scopeguard クレートを使うと、カスタムの Drop 実装を持つ独自の型を定義しなくても、単発の Drop ベースのクリーンアップを簡単に定義できます。

  • ガードはファイル作成の直後に作成されるため、writeln!() が失敗してもファイルは確実にクリーンアップされます。この順序は正しさのために不可欠です。

  • guard()ScopeGuard インスタンスを作成します。これはユーザー定義の値(この場合は path)と、後でその値を受け取るクリーンアップ用クロージャを受け取ります。

  • ガードのクロージャは、ScopeGuard::into_inner無効化 されない限り、スコープ終了時に実行されます(値を取り出すことで、drop 時にガードが何もしなくなります)。成功パスでは into_inner を呼び出すため、ガードはファイルを削除しません。

  • スコープガードは Go の defer 機能に似ています。

  • このパターンは「失敗時のクリーンアップ」のシナリオに最適で、成功パスが明示的に選ばれない限り、デフォルトでクリーンアップを実行すべき場合に適しています。

  • このパターンは、リソースオブジェクトのクリーンアップ戦略を自分で制御できない場合にも有用です。この例では、File::drop() はファイルを閉じますが、削除はしません。

  • scopeguard クレートは、Strategy トレイトを介したクリーンアップ戦略もサポートしています。ガードをアンワインド時のみ、または成功時のみに実行するよう選べるため、単に常に実行するだけではありません。