ZetCode

Golang 错误类型

最后修改时间 2025 年 5 月 8 日

本教程将解释如何在 Go 中使用内置的 error 类型。我们将通过实际示例涵盖错误处理基础以及 Go 程序中正确的错误管理。

error 类型是 Go 中用于错误处理的内置接口。它是 Go 显式错误检查方法而不是异常的基石。error 接口需要一个方法:Error() string

在 Go 中,可能失败的函数通常将其最后一个返回值作为错误返回。约定是在函数调用后立即检查此错误值。这使得错误处理显式且可预测。

基本错误处理示例

最简单的错误处理是检查函数是否返回了错误。此示例演示了 Go 中的基本错误检查。
注意:始终在函数调用后立即处理错误。

basic_error.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 接口来创建自定义错误类型。此示例展示了如何定义和使用自定义错误类型。

custom_error.go
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 动词进行错误包装。此示例展示了如何包装错误以保留上下文。

error_wrapping.go
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 函数用于检查错误链中的特定错误。此示例演示了使用哨兵错误进行错误比较。

error_comparison.go
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)
    }
}

该示例定义了一个哨兵错误 ErrFileNotFounderrors.Is 函数用于检查链中的此特定错误。

Panic 和 Recover 处理意外错误

虽然大多数错误应正常处理,但 panic 和 recover 用于处理真正特殊的情况。此示例展示了正确的 panic/recover 用法。

panic_recover.go
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 语言规范

本教程涵盖了 Go 中的 error 类型,并通过错误处理、自定义错误、包装、比较和恢复的实际示例进行了说明。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Golang 教程