defer chạy khi hàm kết thúc, không phải khi hết một vòng lặp.
Đặt trong loop nghĩa là mọi tài nguyên bị giữ đến tận cuối hàm — vòng lặp vài nghìn file sẽ chạm giới hạn file descriptor.
go
// sai: mọi file mở suốt vòng đời hàm
for _, name := range names {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close() // dồn lại, chỉ chạy khi return
process(f)
}Cách sửa 1 — tách thân loop thành hàm riêng để defer có phạm vi đúng:
go
for _, name := range names {
if err := handle(name); err != nil {
return err
}
}
func handle(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close() // đóng ngay khi handle trả về
return process(f)
}Cách 2 — closure ngay trong loop (func() error { ... }()) nếu ngại tách hàm.
Lưu ý thêm: tham số của defer được tính ngay tại lúc khai báo, chỉ lời gọi mới bị hoãn — nên defer fmt.Println(i) in giá trị i ở thời điểm gặp defer, không phải giá trị cuối.