Golang 错误类型
最后修改时间 2025 年 5 月 8 日
本教程将解释如何在 Go 中使用内置的 error 类型。我们将通过实际示例涵盖错误处理基础以及 Go 程序中正确的错误管理。
error 类型是 Go 中用于错误处理的内置接口。它是 Go 显式错误检查方法而不是异常的基石。error 接口需要一个方法:Error() string。
在 Go 中,可能失败的函数通常将其最后一个返回值作为错误返回。约定是在函数调用后立即检查此错误值。这使得错误处理显式且可预测。
基本错误处理示例
最简单的错误处理是检查函数是否返回了错误。此示例演示了 Go 中的基本错误检查。
注意:始终在函数调用后立即处理错误。
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
divide 函数在尝试除以零时会返回错误。调用者在继续处理结果之前会检查错误。
创建自定义错误类型
我们可以通过实现 error 接口来创建自定义错误类型。此示例展示了如何定义和使用自定义错误类型。
package main
import (
"fmt"
"time"
)
type TimeoutError struct {
Operation string
Timeout time.Duration
}
func (e *TimeoutError) Error() string {
return fmt.Sprintf("%s timed out after %v", e.Operation, e.Timeout)
}
func process() error {
return &TimeoutError{
Operation: "database query",
Timeout: 5 * time.Second,
}
}
func main() {
err := process()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Operation successful")
}
TimeoutError 类型通过有关操作的附加上下文实现了 error 接口。自定义错误提供更详细的错误信息。
使用 fmt.Errorf 包装错误
Go 1.13 引入了使用 fmt.Errorf 和 %w 动词进行错误包装。此示例展示了如何包装错误以保留上下文。
package main
import (
"errors"
"fmt"
"os"
)
func readConfig() error {
_, err := os.ReadFile("config.json")
if err != nil {
return fmt.Errorf("read config: %w", err)
}
return nil
}
func main() {
err := readConfig()
if err != nil {
fmt.Println("Error:", err)
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Failed to access file:", pathErr.Path)
}
}
}
%w 动词在添加上下文的同时包装了原始错误。errors.As 函数用于检查链中的特定错误类型。
使用 errors.Is 比较错误
errors.Is 函数用于检查错误链中的特定错误。此示例演示了使用哨兵错误进行错误比较。
package main
import (
"errors"
"fmt"
"io"
"os"
)
var ErrFileNotFound = errors.New("file not found")
func openFile(name string) error {
_, err := os.Open(name)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return ErrFileNotFound
}
return fmt.Errorf("open file: %w", err)
}
return nil
}
func main() {
err := openFile("missing.txt")
if errors.Is(err, ErrFileNotFound) {
fmt.Println("Custom file not found error")
}
if err != nil {
fmt.Println("Error:", err)
}
}
该示例定义了一个哨兵错误 ErrFileNotFound。errors.Is 函数用于检查链中的此特定错误。
Panic 和 Recover 处理意外错误
虽然大多数错误应正常处理,但 panic 和 recover 用于处理真正特殊的情况。此示例展示了正确的 panic/recover 用法。
package main
import (
"fmt"
"log"
)
func processTask(taskID int) {
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in task %d: %v", taskID, r)
}
}()
if taskID == 0 {
panic("invalid task ID")
}
fmt.Printf("Processing task %d\n", taskID)
}
func main() {
processTask(1)
processTask(0) // This will panic
processTask(2)
fmt.Println("All tasks processed")
}
延迟的 recover 函数可以优雅地处理 panic。这种模式对于必须在错误后继续运行的服务器应用程序很有用。
来源
本教程涵盖了 Go 中的 error 类型,并通过错误处理、自定义错误、包装、比较和恢复的实际示例进行了说明。