Cả hai dùng để kiểm tra error chain (chuỗi lỗi được wrap), nhưng mục đích khác nhau.
errors.Is(err, target) — kiểm tra identity: err có phải (hoặc wrap) target không? Dùng cho sentinel errors (lỗi được định nghĩa sẵn).
go
var ErrNotFound = errors.New("not found")
// Wrap lỗi qua nhiều layers
err := fmt.Errorf("service: %w", fmt.Errorf("repo: %w", ErrNotFound))
errors.Is(err, ErrNotFound) // true — dù wrap 2 lần
errors.Is(err, ErrNotFound) // false nếu dùng %v thay vì %werrors.As(err, &target) — kiểm tra type: err có phải (hoặc wrap) một lỗi thuộc kiểu cụ thể không? Dùng cho custom error types để truy xuất thêm thông tin.
go
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation: %s — %s", e.Field, e.Message)
}
err := fmt.Errorf("handler: %w", &ValidationError{Field: "email", Message: "invalid"})
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println(ve.Field) // "email"
fmt.Println(ve.Message) // "invalid"
}Tóm tắt: errors.Is so sánh giá trị, errors.As so sánh kiểu.
Luôn dùng %w khi wrap để giữ chain.