Appearance
第 13 章:错误处理
13.1 Go 的错误处理理念:没有 try-catch
Go 语言采用了一种不同于其他语言的错误处理方式,它没有 try-catch 语句,而是通过返回值来处理错误。这种设计理念的优点是:
- 错误处理更加明确,代码更加清晰
- 强制开发者处理错误,减少未处理错误的情况
- 错误处理与正常逻辑分离,提高代码可读性
13.2 error 类型使用
error 接口
Go 中的错误是通过 error 接口来表示的:
go
type error interface {
Error() string
}返回错误
函数通常会返回一个 error 类型作为最后一个返回值:
go
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}处理错误
go
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)示例
go
package main
import (
"fmt"
"os"
)
func readFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", err
}
return string(data), nil
}
func main() {
content, err := readFile("test.txt")
if err != nil {
fmt.Printf("Error reading file: %v\n", err)
return
}
fmt.Println("File content:", content)
}13.3 自定义错误
使用 errors.New
go
import "errors"
var ErrNotFound = errors.New("resource not found")
func findUser(id int) (User, error) {
if id <= 0 {
return User{}, ErrNotFound
}
// 查找用户
return User{ID: id, Name: "Alice"}, nil
}使用 fmt.Errorf
go
func processUser(id int) error {
user, err := findUser(id)
if err != nil {
return fmt.Errorf("failed to process user: %w", err)
}
// 处理用户
return nil
}自定义错误类型
go
type AppError struct {
Code int
Message string
}
func (e AppError) Error() string {
return fmt.Sprintf("%d: %s", e.Code, e.Message)
}
func validateInput(input string) error {
if input == "" {
return AppError{Code: 400, Message: "input cannot be empty"}
}
return nil
}13.4 panic 与 recover(异常处理)
panic
panic 用于处理严重错误,会导致程序终止执行:
go
func process() {
panic("something went wrong")
fmt.Println("This line will not execute")
}recover
recover 用于捕获 panic,防止程序崩溃:
go
func safeProcess() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("something went wrong")
}
func main() {
safeProcess()
fmt.Println("Program continues execution")
}何时使用 panic
- 当程序遇到无法恢复的错误时
- 当程序逻辑出现严重错误时
- 当函数的前置条件被违反时
实际应用
go
package main
import "fmt"
func mustDivide(a, b float64) float64 {
if b == 0 {
panic("division by zero")
}
return a / b
}
func safeDivide(a, b float64) float64 {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
return mustDivide(a, b)
}
func main() {
result := safeDivide(10, 0)
fmt.Println("Result:", result) // 输出 0
fmt.Println("Program continues")
}