Go: ループ内の defer は関数終了まで実行されない → クロージャ/関数に切り出す
defer はブロックではなく「関数」の終了時に実行される。ループ内で defer f.Close() すると全ファイルが関数を抜けるまで閉じられず、ファイルディスクリプタが枯渇する。
ダメな例
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で歯止めをかけると安心。