ZetCode

Golang fmt.Errorf 函数

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

本教程将讲解如何在 Go 中使用 fmt.Errorf 函数。我们将通过格式化错误的实际示例,涵盖错误创建基础知识。

fmt.Errorf 函数使用格式化的消息创建错误值。它的工作方式类似于 fmt.Printf,但返回错误而不是打印输出。这对于创建描述性的错误消息非常有用。

在 Go 中,fmt.Errorf 通常用于用额外的上下文包装错误。它支持 fmt.Printf 中所有可用的格式化动词。该函数返回一个实现 error 接口的错误值。

fmt.Errorf 的基本用法示例

fmt.Errorf 最简单的用法是创建一个带有静态消息的错误。此示例演示了基本的错误创建。
注意: 错误消息可以包含任何格式说明符。

basic_error.go
package main

import (
    "fmt"
    "log"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Result:", result)
}

当尝试除以零时,该函数返回一个格式化的错误。main 函数检查错误并在存在时记录它。

格式化错误消息

fmt.Errorf 支持所有标准的格式化动词。此示例展示了如何将动态值包含在错误消息中。

formatted_error.go
package main

import (
    "fmt"
    "os"
)

func checkFileSize(filename string, maxSize int64) error {
    info, err := os.Stat(filename)
    if err != nil {
        return err
    }
    
    if info.Size() > maxSize {
        return fmt.Errorf("file %s is too large (%d bytes > max %d bytes)",
            filename, info.Size(), maxSize)
    }
    return nil
}

func main() {
    err := checkFileSize("data.txt", 1024)
    if err != nil {
        fmt.Println("Error:", err)
    }
}

错误消息包含文件名、实际大小和最大大小。这提供了有关出错内容的详细上下文。

使用 fmt.Errorf 包装错误

fmt.Errorf 可以使用 %w 动词来包装现有错误。此示例展示了如何在保留原始错误的同时添加上下文。

wrapping_error.go
package main

import (
    "errors"
    "fmt"
    "os"
)

func readConfig(file string) ([]byte, error) {
    data, err := os.ReadFile(file)
    if err != nil {
        return nil, fmt.Errorf("failed to read config file %s: %w", file, err)
    }
    return data, nil
}

func main() {
    _, err := readConfig("missing.conf")
    if err != nil {
        fmt.Println("Error:", err)
        
        var pathErr *os.PathError
        if errors.As(err, &pathErr) {
            fmt.Println("Underlying path error:", pathErr)
        }
    }
}

%w 动词在添加上下文的同时包装了原始错误。main 函数仍然可以使用 errors.As 访问底层错误。

包含多个值的错误

fmt.Errorf 可以将多个值格式化到错误消息中。此示例展示了一个包含多个动态值的复杂错误。

multi_value_error.go
package main

import (
    "fmt"
    "time"
)

func scheduleEvent(name string, when time.Time) error {
    if when.Before(time.Now()) {
        return fmt.Errorf("cannot schedule %s at %v (in the past, now is %v)",
            name, when.Format(time.RFC3339), time.Now().Format(time.RFC3339))
    }
    return nil
}

func main() {
    pastTime := time.Now().Add(-1 * time.Hour)
    err := scheduleEvent("meeting", pastTime)
    if err != nil {
        fmt.Println("Error:", err)
    }
}

错误包括事件名称、计划时间和当前时间。所有值都已正确格式化,以便清晰地报告错误。

使用 fmt.Errorf 定义自定义错误类型

fmt.Errorf 可以与自定义错误类型一起使用。此示例展示了如何将格式化消息与结构化错误数据结合起来。

custom_error.go
package main

import (
    "errors"
    "fmt"
)

type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

func validateUser(name string, age int) error {
    if name == "" {
        return fmt.Errorf("user validation: %w",
            ValidationError{Field: "name", Message: "cannot be empty"})
    }
    if age < 0 {
        return fmt.Errorf("user validation: %w",
            ValidationError{Field: "age", Message: "must be positive"})
    }
    return nil
}

func main() {
    err := validateUser("", -1)
    if err != nil {
        fmt.Println("Error:", err)
        
        var valErr ValidationError
        if errors.As(err, &valErr) {
            fmt.Printf("Field %s failed: %s\n", valErr.Field, valErr.Message)
        }
    }
}

自定义的 ValidationError 提供了结构化的错误信息。fmt.Errorf 在添加额外上下文的同时包装了这些错误。

包含位置信息的错误

fmt.Errorf 可以在错误中包含调用者信息。此示例展示了如何创建包含文件和行号详细信息的错误。

position_error.go
package main

import (
    "fmt"
    "runtime"
)

func doSomething() error {
    _, file, line, _ := runtime.Caller(1)
    return fmt.Errorf("error at %s:%d - invalid operation", file, line)
}

func main() {
    err := doSomething()
    if err != nil {
        fmt.Println("Error:", err)
    }
}

runtime.Caller 函数获取调用者的文件和行号。此信息包含在格式化的错误消息中。

条件错误格式化

fmt.Errorf 可以与条件逻辑一起使用以创建不同的错误消息。此示例展示了动态错误消息生成。

conditional_error.go
package main

import (
    "fmt"
    "strings"
)

func processInput(input string) error {
    if len(input) == 0 {
        return fmt.Errorf("input error: empty string provided")
    }
    
    if strings.Contains(input, "error") {
        return fmt.Errorf("input error: contains forbidden word 'error'")
    }
    
    if len(input) > 100 {
        return fmt.Errorf("input error: length %d exceeds maximum of 100", len(input))
    }
    
    return nil
}

func main() {
    tests := []string{"", "test with error", strings.Repeat("a", 101)}
    
    for _, test := range tests {
        err := processInput(test)
        if err != nil {
            fmt.Printf("'%s' → %v\n", test, err)
        }
    }
}

不同的错误条件会生成不同的格式化消息。该函数根据输入验证返回适当的错误。

来源

Go fmt 包文档

本教程通过实际的格式化错误创建和包装示例,介绍了 Go 中的 fmt.Errorf 函数。

作者

我的名字是 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在教学编程方面拥有十多年的经验。

列出所有 Golang 教程