文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

聊聊Golang饱受争议的Error

2024-11-30 05:26

关注

而在我们最爱的Golang中,有这样一个饱受争议的error类型,它不是一个整数,而是一个接口。

package main

import (
    "errors"
    "fmt"
)

type name struct {
    error string
}

func (n *name) Error() string {
    return fmt.Sprintf("%s : ...", n.error)
}

func main() {
    
    err := judge(11)
    //err := judge(1)
    //err := judge(6)
    fmt.Println(err)
}

func judge(num int) error {
    if num > 5 && num < 10 {
        return errors.New("这个数字大于5了..")
    }
    if num >= 10 {
        return fmt.Errorf("%d大于或等于10了...", num)
    }
    return &name{error: "hello"}
}

这是三种可以作为error返回值的方式。errors.New()创建出来的error类型其实是errorString结构体。

// src/errors/errors.go

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
func New(text string) error {
 return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
 s string
}

func (e *errorString) Error() string {
 return e.s
}

所以我们创建的结构体name其实和errors.New()底层的形式是基本一样的。

而使用的fmt.Errorf其实是先将字符串格式化然后再调用errors.New()。

二、error引人争论的点在哪?

在Go中,设计者从语言层面要求人们需要明确地处理遇到的错误,但是因此导致的问题也十分明显,使用Go语言编写的代码中err会到处都是,不过优秀的IDE——Goland能够解决这个问题,使用Goland能够将err!=nil这段判断和处理压缩,不再干扰代码的阅读。

我本人是不太喜欢Java的try-catch机制,可能是不太会用,Go语言官方提到try-catch会让代码变得比较混乱,很多程序员会胡乱catch异常,导致错误处理比较冗长。

而Go语言通过多返回值机制,让返回错误变得很简单,并且提供panic和error两种机制,感觉这种机制更有优势,也看起来更简洁。

煎鱼大佬之前有博客谈到了Go社区中关于Go错误处理的新议题,大家想了解的可以看看:

其实之前Go社区中出现过多种关于错误处理的新议题,但是都没有被采纳...

三、如何优雅的处理错误

1. 避免处理“哨兵错误”,即Sentinel errors

比如为了判断err == io.EOF就得引入io包,这是标准库的包还能接受,如果是第三方库的包,并且使用“哨兵错误”,很容易导致循环引用的问题。

2. 避免使用error类型

虽然这种错误比“哨兵错误”要好,它可以捕获更多关于错误的上下文信息,比如出错的行数等其他字段信息。但是又不可避免地在定义错误和使用错误的包之间形成依赖关系,又容易导致循环引用的问题。

3. 使用不透明的“黑盒错误”
func f() error{
    sentence,err := say.Hello()
    if err != nil{
        return err
    }
    // ...
}

上面这种写法是不是我们经常会用到?这种情况下,我们只需要判断err是否为空,不为空,代表有错误,就直接返回错误,否则就继续执行后面的流程。

作为程序执行者,你没有能力看到程序错误的内部信息,只能知道程序有错或者没有错误。这种错误处理作为一种调试辅助手段还是不错的。

4. 使用Warp和Cause

第三方库github.com/pkg/errors可以输出错误堆栈,并且使用起来很简单,大家可以了解一下。

// Wrap annotates cause with a message.
func Wrap(cause error, message string) error
// Cause unwraps an annotated error.
func Cause(err error) error

下面来介绍Wrap和Cause的使用样例:

func ReadFile(path string) ([]byte, error) {
        f, err := os.Open(path)
        if err != nil {
                return nil, errors.Wrap(err, "open failed")
        }
        defer f.Close()

        buf, err := ioutil.ReadAll(f)
        if err != nil {
                return nil, errors.Wrap(err, "read failed")
        }
        return buf, nil
}

func ReadConfig() ([]byte, error) {
        home := os.Getenv("HOME")
        config, err := ReadFile(filepath.Join(home, ".settings.xml"))
        return config, errors.Wrap(err, "could not read config")
}

func main() {
        _, err := ReadConfig()
        if err != nil {
                fmt.Println(err)
                os.Exit(1)
        }
}

如果ReadConfig()执行失败,就会得到下面这一行十分美观的报错:

could not read config: open failed: open /Users/dfc/.settings.xml: no such file or directory

而如果用fmt.Printf和%+v格式来输出就能看到更清晰、更有层次的错误堆栈:

func main() {
        _, err := ReadConfig()
        if err != nil {
                fmt.Printf("%+v",err)
                os.Exit(1)
        }
}

图片

然后我们再来看Cause的使用。

type temporary interface{
    Temporary() bool
}

// IsTemporary returns true if err is temporary.
func IsTemporary(err error) bool {
        te, ok := errors.Cause(err).(temporary)
        return ok && te.Temporary()
}

当需要检查一个错误与一个特定的值或类型时。比如此处,先用Cause取出错误,做断言,最后调用Temporary(),如果断言失败,ok就会是false,就不会调用右边的Temporary()去执行。

如果 && 运算符左侧的子表达式为 false,则不会检查右侧的表达式。因为只要有一个子表达式为 false,则整个表达式都为 false,所以再检查剩余的表达式会浪费 CPU 时间。这被称为短路评估。

本文转载自微信公众号「 程序员升级打怪之旅」,作者「 丘山子&王中阳Go」,可以通过以下二维码关注。

转载本文请联系「 程序员升级打怪之旅」公众号。

来源:程序员升职加薪之旅内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯