个人觉得 Go 的 error 设计的非常好,为什么还那么多人吐槽?

2023-12-22 10:40:03 +08:00
 wkong

一个程序是否健壮,主要判断是是否对异常有精准处理。

像 Java 异常的处理虽然少写了代码,但是增加了未知性。

Go 虽然多了一些代码,但是很容易写出健壮性的程序。孰轻孰重这不是很明显吗?

22244 次点击
所在节点    程序员
156 条回复
boboaiya3
2023-12-22 11:36:24 +08:00
@wkong #13
try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计?
这个是错误,Java try catch 实现 是基于 c 里面的( non local jump ),这种一种特殊的函数跳转方式,肯定和普通函数执行不一样,性能层面,各方面都存在优势
ShadowPower
2023-12-22 11:36:56 +08:00
可以看看 Rust 的 Result 设计,你会对错误处理有一个全新的认知……
thinkershare
2023-12-22 11:38:13 +08:00
go 这种异常设计,就是没有设计,最原始的 C 就是这种模样。go 的 整个设计是简陋而不是简洁,随着需求的增大,go 一定会变成和 C++一样难看。
43n5Z6GyW39943pj
2023-12-22 11:39:56 +08:00
Java 看日志的时候很狼狈,不说了 等下要被喷
songray
2023-12-22 11:42:20 +08:00
你搞错了几件事.
Java 、JS 、Python 完全可以做和 Go 一样的 err. 以 js 为例, 虽然没有多返回值, 但你每个方法返回 { err: boolean, value: any } 就行.
err 的问题在于代码噪音太大, 进行多次相同操作, 唯一也是最好的办法反而是模拟一个蹩脚的 err monad, 参考 Rob 的文章 errors are values.
这个问题 Go 是可以语言层面进行简化的, 但 go 一直没有这么做.
更直白来说, 如果开发者一直在模拟 monad 或者 try catch , 那 go 官方为什么不做进 language level 呢?
happyxhw101
2023-12-22 11:42:35 +08:00
楼主你 out 了, 不管你怎么想, 你要跟上时代潮流, 现在的潮流是 rust, 所以 ....
bwangel
2023-12-22 11:43:41 +08:00
error 没有栈信息,配合 pkg.errors 的 WithStack 使用,增加了开发者的心智负担。

例如以下调用链

func api()

func service()

func rpc()

api -> service -> rpc

rpc 返回了一个错误,一层一层最终返回给了 api, api 拿到错误后,需要记录日志,它想知道错误是哪里来的,此时就需要栈信息。

目前的解决办法,是 rpc 返回错误时,error 用 pkg.errors ( https://github.com/pkg/errors/blob/master/errors.go) 的 Wrap 包装一下,service 包装时,就不能用 wrap 了,需要用 WithMessage ,要不然会出现两份栈信息。

这要求开发者对代码的层次结构非常清楚,哪些函数是最底层,哪些是上层。

你想想,你刚接手了一个 5w 行的项目,读了三天代码之后开始写一些小 feature 。这时候你能了解清楚哪些函数是最底层吗?这样很容易就写错了,然后错误信息里面可能就有 N 份栈信息。
mainjzb
2023-12-22 11:44:04 +08:00
因为他们没写过 C/C++,没写过 windows api
gam2046
2023-12-22 11:44:23 +08:00
go error 的设计,好是好,但是 error 本质上是个字符串,这玩意不太好。如果可以包含一个类型信息就会好很多。
yazinnnn0
2023-12-22 11:46:05 +08:00
因为现代语言大多可以用 monad 去处理这种问题 (比如 error, nil/null, future/promise)

时髦一点的语言甚至内建了 monad comprehension 去处理 monad 地狱的问题

但是 go 给的方案是 c 派的返回 errno 的方式, 也许 go 的哲学类似于 c, 但是 go boy 跳出来吹 err != nil 是令人困惑的
Ayanokouji
2023-12-22 11:46:10 +08:00
@gitrebase 最外层打印日志也有点问题,如果调用链太深或者封装的 error 太多,还是不方便直接看出错误源,error 不自带调用栈,我觉得这是最难接受的
yazinnnn0
2023-12-22 11:55:06 +08:00
rust 和 go 一样个毛....
rust kotlin fsharp swift 都从 ocaml 里抄了不少设计, dart 未来也会向 ocaml 方向靠

go 有一点 ocaml 的影子吗
otakustay
2023-12-22 12:03:57 +08:00
go 这套显然和 shell 是一样的,有人喜欢 shell 的异常处理吗
qianzanqi
2023-12-22 12:07:02 +08:00
@bwangel 如果 service 有两处调用 rpc 就还得依靠 message 内容来区分调用处了。

我一直觉得 go 处理异常要么无脑 WithStack 这个就相当于 java 的一句一 try ;要么无脑 WithMessage 传个随机字符串,查调用栈只要在项目里搜索就行,还没有运行时的性能问题
flyqie
2023-12-22 12:13:03 +08:00
go 写业务是折磨,错误处理这边啥玩法都有。。
MegrezZhu
2023-12-22 12:14:00 +08:00
要求函数迪调用方每次调用都显式地验证错误是否存在,又不提供语法糖才是问题的根源。
事实上我还挺喜欢这种 error pattern 的,在我司的 C++代码里面大量存在着 StatusOr<RetType>的用法,但它和 Go 不一样的是 C++有宏啊,绝大多数场景下我都不需要写`if (result.ok()) {}`,直接用一些`RETURN_IF_ERROR(func())`, `ASSIGN_OR_RETURN(auto ret, func())`就行了,没有什么重复性的代码要写……
me1onsoda
2023-12-22 12:15:55 +08:00
增加了未知性,这是编程规范层面能解决的。禁止 catch exception runtimeexception 这种模糊的异常。但 go 就很影响编程体验。。
dacapoday
2023-12-22 12:44:51 +08:00
@Ayanokouji 学艺不精,go 的 error 是接口,只是多数人 偷懒 或 习惯使用标准库的 字符串 实现。
想要调用栈完全可以自己加,相关的 error 库也非常多。
关键是 go 给了你选择,运行时收集调用栈是有开销的。
开发一些偏底层的库时,调用栈信息又完全不够,error 接口 意味着可以自定义 数据结构 只收集关键信息。
对于调用者,对 error 接口进行类型推断,又能很方便的滤出 是底层系统 error, 还是自定义 error struct ,还是中间各层业务封装的 字符串 error
nothingistrue
2023-12-22 12:52:47 +08:00
学艺不精。参见
@Ayanokouji #7
@pursuer #9
@songray #44
buffzty
2023-12-22 12:57:11 +08:00
go 的错误处理跟 c 语言一模一样,没有一个人喷 c 但是喷 go 的挺多的
再者说了 c 和 go 都有 try catch 模式,c 里面用 goto ,go 一般用 errgroup 也有可以用 goto

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

https://ex.noerr.eu.org/t/1002535

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

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

© 2021 V2EX