Ba từ khóa này xử lý việc thực thi trì hoãn và các tình huống ngoại lệ. defer lên lịch dọn dẹp, panic kích hoạt một crash runtime (cho các trường hợp thực sự ngoại lệ), và recover có thể bắt một panic để ngăn chương trình crash.
Ba từ khóa này xử lý việc thực thi trì hoãn và các tình huống ngoại lệ. defer lên lịch dọn dẹp, panic kích hoạt một crash runtime (cho các trường hợp thực sự ngoại lệ), và recover có thể bắt một panic để ngăn chương trình crash.
func readFile() error {
f, err := os.Open("file.txt")
if err != nil { return err }
defer f.Close() // ĐẢM BẢO chạy khi readFile trả về (mọi đường thoát)
// ... dùng f, với nhiều điểm return ...
return nil // f.Close() chạy ở đây tự động
}
defer lên lịch một lời gọi hàm để thực thi khi hàm bao quanh trả về — bất kể nó trả về như thế nào (return thông thường, lỗi, hoặc panic). Đây là cách idiomatic để đảm bảo dọn dẹp: đóng file, mở khóa mutex, đóng kết nối — đặt ngay cạnh chỗ cấp phát để bạn không thể quên.
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// in ra: 3, 2, 1 — defer cuối cùng, thực thi đầu tiên (thứ tự stack)
func mustPositive(n int) {
if n < 0 {
panic("negative not allowed") // dừng thực thi bình thường, tháo gỡ stack
}
}
// panic chạy các hàm defer khi nó tháo gỡ, rồi crash chương trình (kèm stack trace)
panic dừng luồng bình thường và tháo gỡ call stack (chạy các hàm defer trên đường đi), cuối cùng crash chương trình. Nó dành cho lỗi lập trình / các điều kiện không thể phục hồi (out-of-bounds, nil dereference, trạng thái bất khả thi) — KHÔNG dành cho lỗi thông thường (những lỗi đó dùng giá trị error trả về).
func safeProcess() {
defer func() {
if r := recover(); r != nil { // recover() trả về giá trị panic
fmt.Println("recovered from:", r) // xử lý nó; chương trình tiếp tục
}
}()
panic("something broke") // panic này bị bắt bởi recover ở trên
}
// safeProcess trả về bình thường thay vì crash
recover giành lại quyền điều khiển một goroutine đang panic — nhưng chỉ hoạt động bên trong một hàm defer. Nó được dùng dè dặt, ví dụ để ngăn một handler request đơn lẻ làm crash cả server.
Thất bại bình thường/dự kiến (file thiếu, input sai, validation) → trả về một giá trị error
panic/recover → dành cho các trường hợp THỰC SỰ ngoại lệ (bug, trạng thái không phục hồi được)
và các ranh giới (ví dụ một server recover để một request lỗi không kết liễu tiến trình)
defer là một tính năng thiết yếu, idiomatic của Go cho dọn dẹp được đảm bảo — đặt việc giải phóng tài nguyên ngay cạnh chỗ cấp phát và đảm bảo nó chạy trên mọi đường return (bao gồm cả panic), giúp ngăn rò rỉ file, khóa và kết nối đáng tin cậy hơn nhiều so với dọn dẹp thủ công.
Hiểu thứ tự LIFO của nó cũng quan trọng. panic/recover cung cấp một cơ chế giống exception, nhưng nguyên tắc then chốt là Go dành chúng cho các tình huống thực sự ngoại lệ, không thể phục hồi — lỗi thông thường luôn nên được trả về dưới dạng giá trị và kiểm tra.
Lạm dụng panic/recover như xử lý exception chung là không idiomatic và là một sai lầm phổ biến cho các lập trình viên đến từ các ngôn ngữ dựa trên exception.
Biết khi nào dùng defer (luôn luôn, cho dọn dẹp), giá trị error trả về (thất bại thông thường), và panic/recover (hiếm, ngoại lệ, hoặc tại các ranh giới) là nền tảng để viết Go đúng đắn, idiomatic và là một chủ đề phỏng vấn thường gặp.