看到好多人吐槽 golang 的错误处理,但我用的很爽啊

2020-09-11 09:22:06 +08:00
 dafsic

golang 的错误处理,我之前也吐槽,但从 1.13 开始吧就挺好用了。 之前吐槽点:

  1. 如果底层函数出错,只在上层打印错误信息,会丢失调用栈,不知道最开始的错误发生在哪里。
  2. 如果通过字符串追加的方式,加入调用栈信息,那么错误类型会丢失,无法像 if err == io.EOF 这样判断是什么错误。

现在已经不是问题了。

// LineInfo 返回调用此函数的代码所在函数、文件、行号
// 此函数应该在一个单独的文件中,比如,utils/getlineinfo.go
func LineInfo() string {
	function := "xxx"
	pc, file, line, ok := runtime.Caller(1)
	if !ok {
		file = "???"
		line = 0
	}
	function = runtime.FuncForPC(pc).Name()
	return fmt.Sprintf(" -> %s():%s:%d", function, file, line)
}

var ErrAuth = errors.New("auth error")
var ErrAccount = fmt.Errorf("%w: account not exist", ErrAuth)
var ErrPassword = fmt.Errorf("%w: incorrect password", ErrAuth)

func login(acc, pwd string) (string, error) {
	if acc != "libai" {
		return "", ErrAccount
	}
	if pwd != "123456" {
		return "", ErrPassword
	}

	return fmt.Sprintf("key:AC34cvG-%d", time.Now().Unix()), nil
}

func getInfo(acc, pwd string) (string, error) {
	key, err := login(acc, pwd)
	if err != nil { // login 的错误
		return "", fmt.Errorf("%w%s", err, LineInfo())
	}

	// 打开下面的注释就会是 key 过期
	//time.Sleep(time.Second)

	msg, err := getIntro(key)
	if err != nil { // key 错误
		return "", fmt.Errorf("%w%s", err, LineInfo())
	}

	return msg, nil
}

var ErrKey = errors.New("invalid key")

func getIntro(key string) (string, error) {
	if key != fmt.Sprintf("key:AC34cvG-%d", time.Now().Unix()) {
		return "", ErrKey
	}

	return "李白,号青莲居士", nil
}

func main() {
	info, err := getInfo("libai", "123456")
	if err != nil && errors.Is(err, ErrAuth) { // 无论账号错误还是密码错误,都是认证错误
		fmt.Printf("[info]%s\n", err.Error())
	} else if err != nil {
		fmt.Printf("[error]:%s\n", err.Error())
	}

	fmt.Println(info)
}

9760 次点击
所在节点    Go 编程语言
74 条回复
ruanimal
2020-09-11 09:36:12 +08:00
不可否认,if err != nil 导致代码的可读性变差,异常处理的逻辑和业务逻辑代码混杂在一起
z0wjqnxi
2020-09-11 09:47:03 +08:00
你说的两个点,第三方的 errors 包都已经足够好用,现在的版本,go 自带的 error.Is(), 判断错误类型也够用了。

我觉得现在最大的痛点应该是要写一堆‘ if err!= nil {}’ ,代码块里可能有很多重复的错误处理逻辑还不好抽离封装, 为了少写这些重复逻辑,社区许多人推荐用 defer,现在我偶尔用 goto 。
sryanyuan
2020-09-11 09:48:08 +08:00
写惯 c/c++的表示错误码很正常,普通错误,比如文件找不着这种,不算异常,就返回一个 errorcode 上层继续处理,程序可以继续正常运行
碰到各种异常,直接崩溃了就得了
damngood
2020-09-11 10:00:06 +08:00
@ruanimal 感觉是各有各的好处, 就地处理还是在 catch 块处理.
dafsic
2020-09-11 10:04:09 +08:00
@z0wjqnxi 我觉得可读性和可维护性更重要,有时候一个功能会用到另一个功能的部分函数,我可能会通过复制代码的方式,使两个功能减少联系。功能划分的合理,可能一个函数里只有一两个 if err != nil{}的地方,一点不影响阅读和美感,每个错误处理逻辑非常清晰,反而更优雅。而且不是所有的错误都要处理,信任边界的问题。总之,这是因人而异的,不是普适的,受人生观价值观的哲学范畴。
MadbookPro
2020-09-11 10:15:02 +08:00
没觉得 if err != nil 有哪里不爽。。 一直写 lua 也是这么处理的
relic
2020-09-11 10:16:29 +08:00
rust 的?才是真香
zsdroid
2020-09-11 10:26:36 +08:00
这不是反人类吗?返回错误时需要同时返回其他非错误的返回值
zoffy
2020-09-11 10:32:50 +08:00
通常来说,代码表达要保持正向语义:isXXX 、==

少数表达可以使用反向语义:disabled 、prevent 、ignore
heiheidewo
2020-09-11 10:35:08 +08:00
挺好用的,有必要强制检查接口返回的正确性
zhuangzhuang1988
2020-09-11 10:37:03 +08:00
开心就好
dafsic
2020-09-11 10:42:40 +08:00
@zsdroid 兄弟,你代表不了全人类,对 windows 用户来说,linux 反人类吧。因人而异的东西不值一提。
no1xsyzy
2020-09-11 10:45:06 +08:00
俺寻思这和 .then(good, bad) 一样吧
JavaScript 已经快进到下一步了。
zjsxwc
2020-09-11 11:03:10 +08:00
还不如直接抄 rust 的 Result<Ok, Err> 配合 ?宏 模式
zsdroid
2020-09-11 11:03:15 +08:00
@dafsic 因人而异的东西不值一提?那你发帖的东西就不是因人而异了?这双标真牛逼!!!!!
CosimoZi
2020-09-11 11:10:58 +08:00
说了多少次, 应该实现为 coproduct 的东西, 愣是实现成了 product. 真是把类型系统喂了狗了.
wysnylc
2020-09-11 11:20:49 +08:00
@zsdroid #15 他即世界中心
cmdOptionKana
2020-09-11 11:22:49 +08:00
很爽倒不至于,多数人会有点小烦,但问题不是很严重,主要是 golang 槽点太少,才经常有人提起这个小缺点。
dafsic
2020-09-11 11:38:51 +08:00
@zsdroid 1.我说的不是 if err != Nil 的问题。2, 你可以说你不喜欢,反人类?你不配。不再回复你了。
no1xsyzy
2020-09-11 11:41:11 +08:00

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/706008

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX