Sentinel error là biến error toàn cục được định nghĩa sẵn, phù hợp cho các điều kiện lỗi đơn giản không cần thêm context. Custom error type phù hợp khi cần mang thêm metadata về lỗi.
go
// Sentinel errors — so sánh bằng errors.Is
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrConflict = errors.New("already exists")
)
// Custom error type — truy xuất metadata qua errors.As
type AppError struct {
Code int
Message string
Err error // wrapped underlying error
}
func (e *AppError) Error() string {
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error { return e.Err } // quan trọng cho error chain
func getUser(id int) (*User, error) {
user, err := db.Find(id)
if err != nil {
return nil, &AppError{Code: 404, Message: "user not found", Err: ErrNotFound}
}
return user, nil
}
// Caller có thể check cả hai cách
err := getUser(99)
errors.Is(err, ErrNotFound) // true — nhờ Unwrap()
var ae *AppError
if errors.As(err, &ae) {
fmt.Println(ae.Code) // 404
}Nguyên tắc: định nghĩa errors trong package, export chúng để caller dùng errors.Is.
Tránh dùng err.Error() == "..." string comparison.
Sentinel errors are predefined package-level error variables, suitable for simple error conditions without extra context. Custom error types carry metadata and should implement Unwrap() to participate in the error chain.
go
// Sentinel — checked with errors.Is
var ErrNotFound = errors.New("not found")
// Custom type — checked with errors.As, carries metadata
type AppError struct {
Code int
Message string
Err error
}
func (e *AppError) Error() string { return fmt.Sprintf("[%d] %s", e.Code, e.Message) }
func (e *AppError) Unwrap() error { return e.Err } // critical for chain traversalRule: export errors from your package for callers to use errors.Is.
Never compare with err.Error() == "some string".