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

本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

目录
  • error的基本用法
    • 1. 获取错误信息
    • 2. 在条件判断中使用
  • 基本区别
    • 1.函数签名
    • 2.使用场景
  • 详细对比
    • 1. 静态错误 vs 动态错误
    • 2. 错误比较
    • 3. 错误包装
  • 实际应用场景
    • 1. 使用 errors.New 的场景
    • 2. 使用 fmt.Errorf 的场景
  • 自定义错误类型
    • 1.基本用法
    • 2.自定义错误类型示例

error的基本用法

1. 获取错误信息

package main

import (
    "errors"
    "fmt"
)

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

2. 在条件判断中使用

package main

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("err: %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  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("错误信息:", 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 struct {
    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  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", validationErr.Field)
    }
    
    if businessErr, ok := err.(*BusinessError); ok {
        fmt.Printf("   这是一个业务错误,错误码: %d\n", businessErr.Code)
    }
}

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

您可能感兴趣的文章:

  • GO语言标准错误处理机制error用法实例
  • 解决启动MongoDB错误:error while loading shared libraries: libstdc++.so.6:cannot open shared object file:
  • 浅析golang开发Error的使用详解
  • Go 自定义error错误的处理方法
  • Go语言中实现打印堆栈的errors包的用法详解
  • golang常用库之pkg/errors包第三方错误处理包案例详解
  • Go error的使用方式详解

相关推荐:

NumPy深拷贝浅拷贝区别_视图(View)与arr.copy()完全复制内存区分

view()不分配新内存而共享数据缓冲区,故修改视图即修改原数组;copy()分配新内存实现一层深拷贝,但object数组需deepcopy才彻底隔离。 为什么 arr.view() 修改原数组? 因为 view() 不分配新内存,只是换了个“眼镜”看同一块内存。它共享数据缓冲区(arr.data),改视图就等于改原数组。 常见错误现象:arr.view().fill(0) 后发现原 arr 全变...

泡姆泡姆官网和steam购买有什么区别 二者区别介绍​

泡姆泡姆官网和steam购买的区别主要在服务器和奖励,官网是官服且有专属奖励,steam无部分限定奖励。​以下为您详细介绍二者区别,感兴趣的玩家请往下看。 泡姆泡姆官网和steam购买区别介绍如下:​ 官网奖励介绍 1、首发特典2组共6件外观(6月16日前均可获取) 2、标准版 3、好多衣服版 4、双人组合包 5、单独购买好多衣眼升级包 6、线上房间码联机 7、本地多人游玩 8、森空岛限定头像 9...

C++动态内存分配 new和malloc区别对比

new是C++运算符,自动调用构造函数并类型安全,malloc是C函数需手动计算内存且不调用构造函数,两者不可混用,推荐new与delete配对并优先使用智能指针。 在C++中,new 和 malloc 都可以用来动态分配内存,但它们在机制、使用方式和功能上存在本质区别。理解这些差异有助于写出更安全、高效的代码。 1. 所属语言与运行机制不同 new 是C++的运算符,属于C++语言的一部分,它在...

大航海时代传说不同国家区别介绍

在《大航海时代传说》中,玩家开局可选择多个国家,每个国家都有其独特优势。一些玩家可能对各国之间的差异不太清楚,下面将详细介绍这些国家的不同之处,供有需要的玩家参考。 《大航海时代传说》各国特点解析 可供选择的国家包括西班牙、葡萄牙、荷兰、法国、英国和威尼斯(台服未开放)。 西班牙的优势在于人口众多,地理位置优越,位于地中海与大西洋交汇处,便于前往加勒比、非洲及北欧等地。缺点是新手较多,高端战力相对...

开球REMATCH测评:填补了拟真足球与休闲体育之间的空白

光是画面风格就透着一股《师父》里那种低多边形美术的既视感,若再把《师父》中行云流水的身体操控与动作表现融入足球题材,那简直是降维打击。而这款新作正是走的这条路子——主打无规则街头足球大乱斗。没有越位,没有裁判,甚至连边界都不存在。球踢到场地边缘还会反弹回来,配合第三人称视角,直接营造出一场节奏飞快的街头对决。只要你能把球送进对方大门,用什么方式都无所谓。守门员可以冲出禁区亲自带球进攻,也能借助墙壁...

2024T1冠军皮肤系列姗姗来迟

在经历了漫长的期待后,拳头游戏终于在8月26日深夜公布了备受瞩目的2024年全球总决赛t1纪念皮肤系列,以此庆祝该战队第五次登顶世界之巅。这套皮肤的发布创下了《英雄联盟》历史上冠军皮肤等待时间最长的纪录。往年,冠军皮肤通常在5月的季中冠军赛期间亮相,而今年却推迟到了新一届全球总决赛开赛前一个月。拳头游戏声称投入了大量心血进行打磨,但这份等待是否值得,最终还需由粉丝们来评判。 早在去年总决赛后的发布...

冰咖啡和热咖啡区别有多大

冰咖啡清凉降暑,保留风味且刺激小,含咖啡因较少,适合夏季饮用;热咖啡抗氧化强,提神减压,促进代谢,有益社交,更适合提神醒脑。 冰咖啡和热咖啡,作为我们日常生活中常见的两种饮品,其实它们在健康影响上存在着一些微妙的差异。首先,我们要明白,咖啡本身含有咖啡因,可以提神醒脑,提升代谢。然而,当咖啡的温度发生改变时,其影响也会有所不同。 一、冰咖啡 冰咖啡,通常是在炎热的天气里更受欢迎,它带有一种清凉的感...

钟馗抓鬼抓的耿鬼?外媒称《黑神话:钟馗》可能更像宝可梦!

在科隆游戏展的开幕夜直播中,游戏科学正式揭晓了《黑神话》系列的第二部作品——《黑神话:钟馗》。 本作延续了将中国传统神话与硬核动作玩法结合的风格。而外媒GameRant则提出了一项有趣推测:这款新作的灵感或许部分源自《宝可梦》。 文章分析指出,在道教传说中,钟馗受命于阎罗王,专职缉拿并驯服邪祟。作为“鬼王”,他不仅具备驱鬼之力,更能统御群鬼,这一设定与“宝可梦训练师”操控精灵的模式惊人相似。鉴于游...

攒钱离职和赎身有啥区别 攒钱离职和赎身有哪些区别

虽然“攒钱离职”和“赎身”都指向离开当前的工作岗位,但两者在心态、过程和最终目标上存在本质区别。前者更像是一场主动规划的职业转型,而后者则带有一种挣脱束缚、寻求解放的紧迫感。理解它们的差异,能帮助我们更清晰地审视自己的职业状态。 一、核心内涵与动机的差异 1、攒钱离职:这是一种主动的、有规划的职业选择。其核心动机通常是积极的,为了追求更好的发展,例如更换到更心仪的行业、创业、继续深造或开启一段间隔...

C#中lock关键字的使用小结

在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块。这是一种简单的同步机制,用来防止多个线程同时访问共享资源或执行需要独占访问的代码段(临界区),...