Go之errors.New和fmt.Errorf 的区别小结

2025-08-18 22:50

本文主要是介绍Go之errors.New和fmt.Errorf 的区别小结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考...

error的基本用法

1. 获取错误信息

package main

import (
    "errors"
    "fmt"
)

func main() {
    // 创建错误
    err := errors.New("文件不存在")
    
    // 获取错误信息
    errorMessage := err.Error()
    fmt.Println("错误信息:", errorMessage) // 输出: 错误信息: 文件不存在
    
    // 直接打印错误
    fmt.Println("直接打印:", err) // 输出: 直接打印: 文件不存在
}

2. 在条件判断中使用

package mahttp://www.chinasem.cnin

import (
    "errors"
    "fmt"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("除数不能为零")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        // 获取错误信息进行判断
        if err.Error() == "除数不能为零" {
            fmt.Println("检测到除零错误")
        }
        
        // 打印错误信息
        fmt.Println("错误:", err.Error())
        return
    }
    fmt.Println("结果:", result)
}

基本区别

1.函数签名

// errors.New - 只接受字符串
func New(text string) error

// fmt.Errorf - 接受格式化字符串和参数
func Errorf(format string, a ...interface{}) error

2.使用场景

package main

import (
    "errors"
    "fmt"
)

func main() {
    // errors.New - 简单静态错误
    err1 := errors.New("文件不存在")
    fmt.Println(err1)
    
    // fmt.Errorf - 动态格式化错误
    filename := "config.txt"
    err2 := fmt.Errorf("文件 %s 不存在", filename)
    fmt.Printf("phperr: %v \n", err.Error())
}

详细对比

1. 静态错误 vs 动态错误

package main

import (
    "errors"
    "fmt"
)

// 使用 errors.New - 适合静态错误
var (
    ErrNotFound = errors.New("记录未找到")
    ErrInvalidInput = errors.New("无效输入")
    ErrTimeout = errors.New("操作超时")
)

// 使用 fmt.Errorf - 适合动态错误
func validateAge(age int) error {
    if age < 0 {
        return fmt.Errorf("年龄不能为负数: %d", age)
    }
    if age > 150 {
        return fmt.Errorf("年龄不能超过150岁: %d", age)
    }
    return nil
}

func main() {
    // 静态错误
    fmt.Println(ErrNotFound)
    
    // 动态错误
    err := validateAge(-5)
    fmt.Println(err)
}

2. 错误比较

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("记录未找到")

func main() {
    // errors.New 创建的错误可以用于比较
    err1 := ErrNotFound
    err2 := ErrNotFound
    
    fmt.Println(err1 == err2) // true
    
    // fmt.Errorf 创建的错误每次都是新的
    err3 := fmt.Errorf("记录未找到")
    err4 := fmt.Errorf("记录未找到")
    
    fmt.Println(err3 == err4) // false
}

3. 错误包装

package main

import (
    "errors"
    "fmt"
)

func processFile(filename string) error {
    // 模拟文件操作错误
    originalErr := errors.New("权限不足")
    
    // 使用 fmt.Errorf 包装错误
    return fmt.Errorf("处理文件 %s 失败: %v", filename, originalErr)
}

func main() {
    err := processFile("config.txt")
    fmt.Println(err) // 输出: 处理文件 config.txt 失败: 权限不足
}

实际应用场景

1. 使用 errors.New 的场景

package main

import (
    "errors"
    "fmt"
)

// 定义包级别的错误常量
var (
    ErrUserNotFound = errors.New("用户不存在")
    ErrInvalidPassword = errors.New("密码错误")
    ErrDatabaseConnection = errors.New("数据库连接失败")
)

func authenticateUser(userID string, password string) error {
    if userID == "" {
        return ErrUserNotFound
    }
    
    if password == "" {
        return ErrInvalidPassword
    }
    
    // 验证逻辑...
    return nil
}

func main() {
    err := authenticateUser("", "123456")
    if err == ErrUserNotFound {
        fmt.Println("用户不存在")
    } else if err == ErrInvalidPassword {
        fmt.Println("密码错误")
    }
}

2. 使用 fmt.Errorf 的场景

package main

import (
    "fmt"
    "strconv"
)

func parseUserID(userIDStr string) (int, error) {
    userID, err := strconv.Atoi(userIDStr)
    if err != nil {
        return 0, fmt.Errorf("解析用户ID失败: %v", err)
    }
    
    if userID <= 0 {
        return 0, fmt.Errorf("用户ID必须大于0,当前值: %d", userID)
    }
    
    return userID, nil
}

func getUserInfo(userIDStr string) error {
    userID, err := parseUserID(userIDStr)
    if err != nil {
        return fmt.Errorf("获取用户信息失败: %v", err)
    }
    
    fmt.Printf("用户ID: %d\n", userID)
    return nil
}

func main() {
    err := getUserInfo("abc")
    if err != nil {
        fmt.Println("错误:", err)
    }
}

自定义错误类型

1.基本用法

package main

import "fmt"

// 自定义错误类型
type ValidationError struct {
    Field   string
    Message string
}

// 实现 Error() 方法
func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证失败: 字段 '%s' - %s", e.Field, e.Message)
}

func validateUser(name string) error {
    if name == "" {
        return &ValidationError{
            Field:   "name",
            Message: "用户名不能为空",
        }
    }
    return nil
}

func main() {
    err := validateUser("")
    if err != nil {
        // 获取错误信息
        fmt.Println("错DcBsXTjG误信息:", err.Error())
        
        // 类型断言获取详细信息
        if validationErr, ok := err.(*ValidationError); ok {
            fmt.Printf("字段: %s, 消息: %s\n", validationErr.Field, validationErr.Message)
        }
    }
}

2.自定义错误类型示例

package main

import (
    "fmt"
    "time"
)

// 1. 基础自定义错误类型
type ValidationError struct {
    Field   string
    Message string
    Value   interface{}
}

// 实现 error 接口
func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证失败: 字段 '%s' 的值 '%v' - %s", e.Field, e.Value, e.Message)
}

// 2. 业务错误类型
type BusinessError struct {
    Code    int
    Message string
    Time    time.Time
}

func (e *BusinessError) Error() string {
    return fmt.Sprintf("[%d] %s (时间: %s)", e.Code, e.Message, e.Time.Format("2006-01-02 15:04:05"))
}

// 3. 网络错误类型
type NetworkError struct {
    URL     string
    Status  int
    Message string
}

func (e *NetworkError) Error() string {
    return fmt.Sprintf("网络请求失败: %s (状态码: %d) - %s", e.URL, e.Status, e.Message)
}

// 4. 复合错误类型
type CompositeError strujsct {
    Errors []error
    Context string
}

func (e *CompositeError) Error() string {
    if len(e.Errors) == 0 {
        return fmt.Sprintf("复合错误: %s", e.Context)
    }
    
    result := fmt.Sprintf("复合错误 [%s]:\n", e.Context)
    for i, err := range e.Errors {
        result += fmt.Sprintf("  %d. %s\n", i+1, err.Error())
    }
    return result
}

// 5. 带错误码的错误类型
type CodedError struct {
    Code    int
    Message string
    Details map[string]interface{}
}

func (e *CodedError) Error() string {
    if len(e.Details) == 0 {
        return fmt.Sprintf("[%d] %s", e.Code, e.Message)
    }
    return fmt.Sprintf("[%d] %s - 详情: %v", e.Code, e.Message, e.Details)
}

// 使用示例函数
func validateUser(name string, age int) error {
    if name == "" {
        return &ValidationError{
            Field:   "name",
            Message: "用户名不能为空",
            Value:   name,
        }
    }
    
    if age < 0 || age > 150 {
        return &ValidationError{
            Field:   "age",
            Message: "年龄必须在 0-150 之间",
            Value:   age,
        }
    }
    
    return nil
}

func processOrder(orderID string) error {
    if orderID == "" {
        return &BusinessError{
            Code:    1001,
            Message: "订单ID不能为空",
            Time:    time.Now(),
        }
    }
    
    // 模拟网络请求失败
    if orderID == "invalid" {
        return &NetworkError{
            URL:     "https://api.example.com/orders/" + orderID,
            Status:  404,
            Message: "订单不存在",
        }
    }
    
    return nil
}

func complexOperation() error {
    var errors []error
    
    // 模拟多个操作
    if err := validateUser("", 25); err != nil {
        errors = append(errors, err)
    }
    
    if err := processOrder(""); err != nil {
        errors = append(errors, err)
    }
    
    if len(errors) > 0 {
        return &CompositeError{
            Errors:  errors,
            Context: "用户注册操作",
        }
    }
    
    return nil
}

func main() {
    fmt.Println("=== 自定义错误类型示例 ===\n")
    
    // 1. 验证错误示例
    fmt.Println("1. 验证错误:")
    if err := validateUser("", 25); err != nil {
        fmt.Printf("   %s\n", err.Error())
    }
    
    if err := validateUser("张三", 200); err != nil {
        fmt.Printf("   %s\n", err.Error())
    }
    
    // 2. 业务错误示例
    fmt.Println("\n2. 业务错误:")
    if err := processOrder(""); err != nil {
        fmt.Printf("   %s\n", err.Error())
    }
    
    // 3. 网络错误示例
    fmt.Println("\n3. 网络错误:")
    if err := processOrder("invalid"); err != nil {
        fmt.Printf("   %s\n", err.Error())
    }
    
    // 4. 复合错误示例
    fmt.Println("\n4. 复合错误:")
    if err := complexOperation(); err != nil {
        fmt.Printf("   %s\n", err.Error())
    }
    
    // 5. 带错误码的错误示例
    fmt.Println("\n5. 带错误码的错误:")
    codedErr := &CodedError{
        Code:    5001,
        Message: "数据库连接失败",
        Details: map[string]interface{}{
            "host":     "localhost:3306",
            "database": "users",
            "reason":   "连接超时",
        },
    }
    fmt.Printf("   %s\n", codedErr.Error())
    
    // 6. 错误类型检查示例
    fmt.Println("\n6. 错误类型检查:")
    err := validateUser("", 25)
    if validationErr, ok := err.(*ValidationError); ok {
        fmt.Printf("   这是一个验证错误,字段: %s\n", validationErrpython.Field)
    }
    
    if businessErr, ok := err.(*BusinessError); ok {
        fmt.Printf("   这是一个业务错误,错误码: %d\n", businessErr.Code)
    }
}

到此这篇关于Go之errors.New 和 fmt.Errorf 的区别的文章就介绍到这了,更多相关Go errors.New 和 fmt.Errorf 内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Go之errors.New和fmt.Errorf 的区别小结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155801

相关文章

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作

Go中select多路复用的实现示例

《Go中select多路复用的实现示例》Go的select用于多通道通信,实现多路复用,支持随机选择、超时控制及非阻塞操作,建议合理使用以避免协程泄漏和死循环,感兴趣的可以了解一下... 目录一、什么是select基本语法:二、select 使用示例示例1:监听多个通道输入三、select的特性四、使用se

Go语言使用Gin处理路由参数和查询参数

《Go语言使用Gin处理路由参数和查询参数》在WebAPI开发中,处理路由参数(PathParameter)和查询参数(QueryParameter)是非常常见的需求,下面我们就来看看Go语言... 目录一、路由参数 vs 查询参数二、Gin 获取路由参数和查询参数三、示例代码四、运行与测试1. 测试编程路

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

C#异步编程ConfigureAwait的使用小结

《C#异步编程ConfigureAwait的使用小结》本文介绍了异步编程在GUI和服务器端应用的优势,详细的介绍了async和await的关键作用,通过实例解析了在UI线程正确使用await.Conf... 异步编程是并发的一种形式,它有两大好处:对于面向终端用户的GUI程序,提高了响应能力对于服务器端应

MySQL慢查询工具的使用小结

《MySQL慢查询工具的使用小结》使用MySQL的慢查询工具可以帮助开发者识别和优化性能不佳的SQL查询,本文就来介绍一下MySQL的慢查询工具,具有一定的参考价值,感兴趣的可以了解一下... 目录一、启用慢查询日志1.1 编辑mysql配置文件1.2 重启MySQL服务二、配置动态参数(可选)三、分析慢查

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c