テック技報最新号へ →
コード

Go: ループ内の defer は関数終了まで実行されない → クロージャ/関数に切り出す

defer はブロックではなく「関数」の終了時に実行される。ループ内で defer f.Close() すると全ファイルが関数を抜けるまで閉じられず、ファイルディスクリプタが枯渇する。

@Saharshbugging · 投稿 2026-10-03
#go#defer#リソース管理#lint

ダメな例

go
func processAll(paths []string) error {
    for _, p := range paths {
        f, err := os.Open(p)
        if err != nil {
            return err
        }
        defer f.Close() // ❌ processAll が終わるまで閉じられない
        // ... f を処理 ...
    }
    return nil
}

直し方

go
func processAll(paths []string) error {
    for _, p := range paths {
        if err := processOne(p); err != nil {
            return err
        }
    }
    return nil
}

func processOne(p string) error {
    f, err := os.Open(p)
    if err != nil {
        return err
    }
    defer f.Close() // ✅ processOne を抜けるたびに閉じられる
    // ... f を処理 ...
    return nil
}
  • 小さい処理なら無名関数 func() { ...; defer f.Close(); ... }() で囲んでもよい。

注意点

  • 書き込み用ファイルは Close() のエラーも確認する(defer で握りつぶさない)。
  • linter で検出できる(staticcheck の SA9001、golangci-lint 経由の gocritic deferInLoop など)。AIエージェントに書かせたコードにもlintで歯止めをかけると安心。

出典

X の元の投稿を開く