自定义错误 #

一、实现error接口 #

1.1 基本实现 #

go
type MyError struct {
    Message string
}

func (e *MyError) Error() string {
    return e.Message
}

1.2 使用自定义错误 #

go
func doSomething() error {
    return &MyError{Message: "something went wrong"}
}

二、自定义错误类型 #

2.1 带更多信息的错误 #

go
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation error on field %s: %s", e.Field, e.Message)
}

func validateUser(name string) error {
    if name == "" {
        return &ValidationError{
            Field:   "name",
            Message: "cannot be empty",
        }
    }
    return nil
}

2.2 带错误码的错误 #

go
type ErrorCode int

const (
    ErrNotFound ErrorCode = iota + 1
    ErrInvalidInput
    ErrUnauthorized
)

type AppError struct {
    Code    ErrorCode
    Message string
}

func (e *AppError) Error() string {
    return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}

2.3 带堆栈信息的错误 #

go
type StackError struct {
    Message string
    Stack   []byte
}

func (e *StackError) Error() string {
    return fmt.Sprintf("%s\n%s", e.Message, e.Stack)
}

func NewStackError(msg string) error {
    return &StackError{
        Message: msg,
        Stack:   debug.Stack(),
    }
}

三、错误类型断言 #

3.1 类型断言 #

go
err := validateUser("")

if ve, ok := err.(*ValidationError); ok {
    fmt.Printf("Field: %s, Message: %s\n", ve.Field, ve.Message)
}

3.2 类型switch #

go
switch e := err.(type) {
case *ValidationError:
    fmt.Printf("Validation: %s\n", e.Field)
case *AppError:
    fmt.Printf("App Error: %d\n", e.Code)
default:
    fmt.Println("Unknown error")
}

3.3 errors.As #

go
var ve *ValidationError
if errors.As(err, &ve) {
    fmt.Printf("Field: %s\n", ve.Field)
}

四、实际应用 #

4.1 HTTP错误 #

go
type HTTPError struct {
    StatusCode int
    Message    string
}

func (e *HTTPError) Error() string {
    return fmt.Sprintf("HTTP %d: %s", e.StatusCode, e.Message)
}

func NewHTTPError(status int, msg string) *HTTPError {
    return &HTTPError{
        StatusCode: status,
        Message:    msg,
    }
}

func handleRequest(w http.ResponseWriter, r *http.Request) error {
    if !isAuthenticated(r) {
        return NewHTTPError(http.StatusUnauthorized, "unauthorized")
    }
    return nil
}

4.2 数据库错误 #

go
type DBError struct {
    Query   string
    Message string
    Cause   error
}

func (e *DBError) Error() string {
    return fmt.Sprintf("DB error: %s (query: %s)", e.Message, e.Query)
}

func (e *DBError) Unwrap() error {
    return e.Cause
}

4.3 业务错误 #

go
type BusinessError struct {
    Code    string
    Message string
}

func (e *BusinessError) Error() string {
    return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}

var (
    ErrUserNotFound = &BusinessError{Code: "USER_001", Message: "User not found"}
    ErrInvalidInput = &BusinessError{Code: "INPUT_001", Message: "Invalid input"}
)

五、最佳实践 #

5.1 提供有用信息 #

go
type Error struct {
    Op  string
    Err error
}

func (e *Error) Error() string {
    return fmt.Sprintf("%s: %v", e.Op, e.Err)
}

5.2 实现Unwrap #

go
func (e *MyError) Unwrap() error {
    return e.Cause
}

5.3 使用errors.As检查 #

go
var appErr *AppError
if errors.As(err, &appErr) {
    // 处理特定错误
}

六、总结 #

自定义错误要点:

方法 说明
实现Error() 满足error接口
类型断言 检查错误类型
errors.As 安全的类型检查
Unwrap 支持错误解包

关键点:

  1. 实现接口:实现Error() string方法
  2. 丰富信息:添加字段提供更多上下文
  3. 类型检查:使用errors.As安全检查
  4. 错误码:定义错误码便于处理
  5. Unwrap:支持错误链

准备好学习错误包装了吗?让我们进入下一章!

最后更新:2026-03-26