Go语言错误处理最佳实践
Go语言错误处理最佳实践错误处理是Go语言编程中非常重要的一部分。本文将深入探讨Go语言的错误处理机制和最佳实践。一、错误处理基础1.1 error接口type error interface { Error() string } func New(text string) error { return errorString{text} } type errorString struct { s string } func (e *errorString) Error() string { return e.s }1.2 基本错误处理模式func divide(a, b float64) (float64, error) { if b 0 { return 0, fmt.Errorf(division by zero) } return a / b, nil } func main() { result, err : divide(10, 0) if err ! nil { fmt.Println(Error:, err) return } fmt.Println(Result:, result) }1.3 错误类型type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf(Error %d: %s, e.Code, e.Message) } func process(input string) error { if input { return MyError{Code: 1001, Message: empty input} } return nil }二、错误处理策略2.1 错误检查模式// 模式1直接返回错误 func doSomething() error { err : step1() if err ! nil { return err } err step2() if err ! nil { return err } return step3() } // 模式2提前返回 func processFile(path string) error { file, err : os.Open(path) if err ! nil { return fmt.Errorf(failed to open file: %w, err) } defer file.Close() data, err : io.ReadAll(file) if err ! nil { return fmt.Errorf(failed to read file: %w, err) } return processData(data) }2.2 错误包装func fetchData(url string) error { resp, err : http.Get(url) if err ! nil { return fmt.Errorf(http request failed: %w, err) } defer resp.Body.Close() if resp.StatusCode ! http.StatusOK { return fmt.Errorf(unexpected status code: %d, resp.StatusCode) } return nil } func main() { err : fetchData(http://example.com) if err ! nil { fmt.Printf(Error: %v\n, err) var urlErr *url.Error if errors.As(err, urlErr) { fmt.Printf(URL error: %v\n, urlErr.URL) } } }2.3 错误判断func isTimeoutError(err error) bool { var timeoutErr interface{ Timeout() bool } return errors.As(err, timeoutErr) timeoutErr.Timeout() } func isNotFoundError(err error) bool { return errors.Is(err, os.ErrNotExist) } func main() { err : doSomething() if isTimeoutError(err) { fmt.Println(Operation timed out) } else if isNotFoundError(err) { fmt.Println(Resource not found) } else if err ! nil { fmt.Printf(Unexpected error: %v\n, err) } }三、自定义错误类型3.1 创建自定义错误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 (e *ValidationError) Is(target error) bool { _, ok : target.(*ValidationError) return ok } type DatabaseError struct { Query string Err error } func (e *DatabaseError) Error() string { return fmt.Sprintf(database error on query %s: %v, e.Query, e.Err) } func (e *DatabaseError) Unwrap() error { return e.Err }3.2 使用自定义错误func validateUser(user User) error { if user.Name { return ValidationError{Field: Name, Message: name cannot be empty} } if user.Email { return ValidationError{Field: Email, Message: email cannot be empty} } if user.Age 18 { return ValidationError{Field: Age, Message: must be at least 18} } return nil } func saveUser(user User) error { err : validateUser(user) if err ! nil { return fmt.Errorf(validation failed: %w, err) } query : fmt.Sprintf(INSERT INTO users VALUES (%s, %s, %d), user.Name, user.Email, user.Age) _, err db.Exec(query) if err ! nil { return DatabaseError{Query: query, Err: err} } return nil }四、错误处理最佳实践4.1 不要忽略错误// 不好的做法忽略错误 func badExample() { file, _ : os.Open(data.txt) // 忽略错误 defer file.Close() data, _ : io.ReadAll(file) // 忽略错误 fmt.Println(string(data)) } // 好的做法处理错误 func goodExample() error { file, err : os.Open(data.txt) if err ! nil { return fmt.Errorf(failed to open file: %w, err) } defer file.Close() data, err : io.ReadAll(file) if err ! nil { return fmt.Errorf(failed to read file: %w, err) } fmt.Println(string(data)) return nil }4.2 错误信息应该有意义// 不好的做法模糊的错误信息 func badError() error { return errors.New(something went wrong) } // 好的做法具体的错误信息 func goodError() error { return fmt.Errorf(failed to connect to database at %s: %w, localhost:5432, connectionErr) }4.3 错误链func doWork() error { err : fetchData() if err ! nil { return fmt.Errorf(work failed: %w, err) } return nil } func fetchData() error { err : makeRequest() if err ! nil { return fmt.Errorf(fetch failed: %w, err) } return nil } func makeRequest() error { return fmt.Errorf(network error) } func main() { err : doWork() fmt.Println(err) // work failed: fetch failed: network error }4.4 错误日志func processRequest(req Request) error { err : validateRequest(req) if err ! nil { log.Printf(Validation failed for request %s: %v, req.ID, err) return err } err executeRequest(req) if err ! nil { log.Printf(Execution failed for request %s: %v, req.ID, err) return fmt.Errorf(request execution failed: %w, err) } return nil }五、panic与recover5.1 panic的使用场景func main() { defer func() { if r : recover(); r ! nil { log.Printf(Recovered from panic: %v, r) } }() process() } func process() { data : getData() if data nil { panic(data is nil) } // 继续处理... }5.2 何时使用panic// 可以使用panic的场景 func init() { config, err : loadConfig() if err ! nil { panic(fmt.Sprintf(failed to load config: %v, err)) } appConfig config } // 不应该使用panic的场景 func getUser(id string) (User, error) { user, err : db.GetUser(id) if err ! nil { return User{}, fmt.Errorf(failed to get user: %w, err) // 返回错误不要panic } return user, nil }六、错误处理模式6.1 哨兵错误var ( ErrNotFound errors.New(not found) ErrTimeout errors.New(timeout) ErrInvalid errors.New(invalid input) ) func findUser(id string) (User, error) { user, ok : users[id] if !ok { return User{}, ErrNotFound } return user, nil } func main() { user, err : findUser(123) if err ErrNotFound { fmt.Println(User not found) } else if err ! nil { fmt.Printf(Unexpected error: %v\n, err) } }6.2 错误类型断言func handleError(err error) { var validationErr *ValidationError var dbErr *DatabaseError switch { case errors.As(err, validationErr): fmt.Printf(Validation error: %s\n, validationErr.Field) case errors.As(err, dbErr): fmt.Printf(Database error: %s\n, dbErr.Query) default: fmt.Printf(Unknown error: %v\n, err) } }6.3 错误分组type MultiError struct { errors []error } func (e *MultiError) Add(err error) { e.errors append(e.errors, err) } func (e *MultiError) Error() string { var sb strings.Builder sb.WriteString(multiple errors: ) for i, err : range e.errors { if i 0 { sb.WriteString(; ) } sb.WriteString(err.Error()) } return sb.String() } func (e *MultiError) HasErrors() bool { return len(e.errors) 0 } func validateForm(form Form) error { var me MultiError if form.Name { me.Add(ValidationError{Field: Name, Message: cannot be empty}) } if form.Email { me.Add(ValidationError{Field: Email, Message: cannot be empty}) } if form.Password { me.Add(ValidationError{Field: Password, Message: cannot be empty}) } if me.HasErrors() { return me } return nil }七、总结Go语言的错误处理机制强调显式处理error接口基础错误类型通过Error()方法返回错误信息错误包装使用fmt.Errorf和%w包装原始错误错误判断使用errors.Is和errors.As进行错误类型判断自定义错误创建特定领域的错误类型最佳实践不要忽略错误、提供有意义的错误信息、正确包装错误panic/recover仅用于不可恢复的错误掌握这些技巧可以编写出健壮、可维护的错误处理代码。