第 13 章 / 共 20 章
错误处理
error 接口、errors.Is/As、panic/recover
error 接口
Go 不用异常,函数把错误作为最后一个返回值显式返回。error 是只含一个 Error() string 方法的接口。
package main
import (
"fmt"
"os"
)
func readSize(path string) (int64, error) {
f, err := os.Open(path)
if err != nil {
return 0, fmt.Errorf("open %s: %w", path, err) // 用 %w 包装
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return 0, err
}
return info.Size(), nil
}
func main() {
n, err := readSize("/etc/hostname")
fmt.Println(n, err)
}errors.Is 与 errors.As
标准库 errors 包提供匹配工具,能穿透 %w 的多层包装。
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
func main() {
_, err := os.Open("/no/such/file")
if errors.Is(err, fs.ErrNotExist) {
fmt.Println("file missing")
}
var pe *fs.PathError
if errors.As(err, &pe) {
fmt.Println("path:", pe.Path, "op:", pe.Op)
}
}自定义错误类型
package main
import "fmt"
type ValidationError struct {
Field, Reason string
}
func (e *ValidationError) Error() string {
return e.Field + ": " + e.Reason
}
func validate(name string) error {
if name == "" {
return &ValidationError{Field: "name", Reason: "empty"}
}
return nil
}
func main() {
fmt.Println(validate(""))
}panic / recover
panic 是真正的“异常”,会沿调用栈展开。除非确实无法继续,否则别用。recover 必须在 defer 内才能捕获 panic。
package main
import "fmt"
func risky() {
panic("boom")
}
func safe() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
risky()
return
}
func main() {
fmt.Println(safe())
}