一个程序是否健壮,主要判断是是否对异常有精准处理。
像 Java 异常的处理虽然少写了代码,但是增加了未知性。
Go 虽然多了一些代码,但是很容易写出健壮性的程序。孰轻孰重这不是很明显吗?
1
CrazyMonkeyV 362 天前 7
你对 JAVA 的异常处理有误解。
|
2
dongtingyue 362 天前 6
javar 比较多他们习惯自己那一套,用其他语言就爱用原来的套到新的上面。
|
3
wkong OP @CrazyMonkeyV 什么误解?我也做过几年的 Java 开发
|
4
mars2023 362 天前
Java 的异常,应该对比 go 的 Panic 吧。
|
6
Goooooos 362 天前 1
你说的都对
|
7
Maboroshii 362 天前 2
我比较喜欢 python 的异常,没有个 10 层缩进那叫写代码吗? doge
|
8
Ayanokouji 362 天前 21
1. go 的 error 类型太弱,只能靠字符串判断
2. 多层 error ,如果直接返回,溯源太难(原生 error 无调用栈),如果追加信息,就会有 1 的问题,是什么错误类型难判断 3. 如果不介意多代码,java 完全可以全部是 checked exception ,自带类型和调用栈 |
9
nagisaushio 362 天前 7
因为本可以更好,rust 和 go 的异常原理是一样的,但 rust 的语法糖就很香。但凡 go 多个语法糖也不会这么多人吐槽
|
10
pursuer 362 天前 3
虽然不知道第几次看到类似讨论了...throw catch 是一种函数多级退出的在控制流上语法糖,go 里对应的是 panic ,Java 你写个 return Multivalue<Result,Error?>也不是不行
|
11
cyp0633 362 天前
离 rust 就缺那么一点儿
|
12
zihuyishi 362 天前
其实这个错误处理很早的时候 windows 的 com 组件就是这么设计的,所有的 com 相关 c++接口都返回一个 HRESULT 需要处理,然后这个 HRESULT 在 c#和 vb 这种语言表现就是抛异常。包括后来 javascript 的 promise 也是差不多,最开始是.catch()处理,在 async 函数里就是 try catch
golang 比较特别的其实在于你不好忽略一个 error ,不处理编译不通过,直接用'_'忽略 static check 也会抱怨,导致用户不得不处理这个错误,虽然有些烦,但是我认为是很好的设计。反倒是之前太多人错误处理都太草率了 |
13
wkong OP @Ayanokouji
1. try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计。 2. 多层 error ,可以在最底层的 error 打个 log ,就知道来源了 3. 不仅仅是多写代码问题 |
14
musi 362 天前 4
一个函数里面加了十几个 if 就是为了判断程序有没有错。。。
|
15
xiangyuecn 362 天前 1
On Error Resume Next
On Error GoTo 0 @Maboroshii 那就不得不提上古时期的 vb 语法了,搁现在也是很优雅的,比 python 的强制缩进好看 100 倍 至少人家还有 end 明确收尾😅 |
16
rrfeng 362 天前 via Android
感觉就缺个枚举类型
如果有,可以把业务 error 都用枚举写出来,就不存在字符串判断的问题。 |
17
MoYi123 362 天前
其实是认为 resp, err 这样的写法有 4 种情况
1. nil, err 2. nil, nil 3. resp, err 4. resp, nil 而实际上只有 1 和 4 是比较标准的格式, 没有在语言层面上禁止 2,3 这 2 种写法, 虽然我觉得根本无所谓. |
18
Ayanokouji 362 天前
@wkong 你去看看多少代码 if err!=nil 直接 return 的,一个业务那么多 error ,怎么确定哪个是底层已经打印日志了。
调用其他同事的方法,难道还要去看看他写的源码,error 返回的是啥,看看 fmt.errorf("")里边写的啥? |
19
cmdOptionKana 362 天前
@musi 如果认为没必要判断处理,完全可以让它 panic ,并非全部错误都要特殊处理。语言是死的,人是活的。
|
20
ho121 362 天前 via Android
有人写过 C 语言中的错误处理吗?
|
22
littlewing 362 天前
写 C/C++ 的都没说什么呢?
大部分 C++ 工程代码是不会用 exception 那一套的 |
23
mightybruce 362 天前
因为大多数这种人都是写 java 的,喜欢这种 try catch 异常处理, 对于非业务开发,懒散和滥用 try catch 处理可以导致致命的 bug ,
linux 内核 和 c 那么多库都没有 try catch, 方式比 go 还原始也没啥人说什么,都是业务开发太闲了。 go 的哲学就是 错误就是要显示检查处理的,异常处理的不可见错误检查所带来的问题其实是很大的。不少业务开发才不管那么多,还不是能跑就行,最多加个单元测试。 |
24
nomagick 362 天前 4
你对“设计”这个词是不是有什么误解...
Golang 的异常处理只能说是没有设计,而不能说是设计得好或设计得不好 |
25
Goooooos 362 天前 2
@mightybruce try catch 不是 java 特有,python ,c#,javascript 都有
|
26
hedwi 362 天前
你说得对 很多人其实并没有处理所有的可能出现的错误
|
27
xiuxian 362 天前
@littlewing 我写 C++也基本不用,exception 使用起来是为了取代返回多个错误码,一个个的去判断。 实际上不能像 java 和 go 一样,全局捕获异常。该 carsh 还是得 carsh.
|
28
xiuxian 362 天前
crash
|
29
mightybruce 362 天前
另外 try catch exception 还涉及到 RAII 机制,go 连对象都没有,谈 try catch 在 go 里面是没什么意义的,最多也只是语法糖。
|
30
CloveAndCurrant 362 天前
其实 go 的 error 相比 rust 的最大缺陷就是不能自动传导,rust 使用?就可以将 error 传导到函数最终结果,go 需要手动才能做到。
|
31
joycelin 362 天前 4
是这样的,以前也说不需要泛型,我看以后新的错误处理方式出来了你再怎么说
|
32
gitrebase 362 天前
@Ayanokouji #18 写 Go 一般在最外层打日志吧,比如在 handler ;“if err!=nil 直接 return”在我的工作经历中,几乎不会有这样的代码,多少都会带点上下文
|
33
CloveAndCurrant 362 天前
@CloveAndCurrant 除了 rust ,java ,Python 这些都可以做到将异常自动传导的
|
34
chendy 362 天前
看具体工作内容吧
要是写什么底层逻辑这套东西可能是好用的(因为我没写过) 但是写业务弄这么复杂的错误处理逻辑,系统和开发必死一个 |
35
mcfog 362 天前
你看,即使在这楼里偏支持的声音,也有挺多并不太理解比较现代的那一版 errors.Is/As 新设计,还有如何结合 golang 偏鸭子类型的 interface 设计做复杂 error 交互的同学的。所以我是觉得 Golang 的 error 设计唯一的问题就是可能低估了大家学习一个和传统非常不一样(但是确实精妙)的设计这件事情的成本。
也包括相关的 interface 设计,至少我感觉多数人都要教,而且教了以后也不像其他概念基本一次性掌握,得反复 review 。 |
36
june4 362 天前
一般程序中很少处理异常,都是默认往上抛,直到顶层处理,比如直接返回 5xx 错误,所以 java 这种就不干扰正常代码。
少量函数天生大概率需要就近处理异常,这些函数你再用错误码也不迟,比如我最近看的 js 验证处理库每个函数都有二个版本,一个是 xxx()抛异常,一个是 safeXXX()无异常会 return 错误码,这不二全了吗 |
37
mcfog 362 天前 1
@june4 现在有了 generic 可以搞一个非常棒的 helper
func Must[T any](v T, err error) T { if err != nil { panic(err) } return v } 就是泛化的 https://pkg.go.dev/html/template#Must |
40
zhanshen1614 362 天前
大多数人习惯 try catch 捕获异常肯定会觉得 go 的异常处理不方便。go 的异常处理比较麻烦但很详细可以知道是哪个步骤错误,阅读更清晰。try catch 捕获异常要考虑 exception 的从属关系,而且抛出异常也不一定知道是哪里执行错了还得看报错的文件、所在行,需要借助错误日志定位问题。
|
41
boboaiya3 362 天前
@wkong #13
try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计? 这个是错误,Java try catch 实现 是基于 c 里面的( non local jump ),这种一种特殊的函数跳转方式,肯定和普通函数执行不一样,性能层面,各方面都存在优势 |
42
ShadowPower 362 天前
可以看看 Rust 的 Result 设计,你会对错误处理有一个全新的认知……
|
43
thinkershare 362 天前 1
go 这种异常设计,就是没有设计,最原始的 C 就是这种模样。go 的 整个设计是简陋而不是简洁,随着需求的增大,go 一定会变成和 C++一样难看。
|
44
43n5Z6GyW39943pj 362 天前
Java 看日志的时候很狼狈,不说了 等下要被喷
|
45
songray 362 天前 4
你搞错了几件事.
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 呢? |
46
happyxhw101 362 天前
楼主你 out 了, 不管你怎么想, 你要跟上时代潮流, 现在的潮流是 rust, 所以 ....
|
47
bwangel 362 天前 3
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 份栈信息。 |
48
mainjzb 362 天前
因为他们没写过 C/C++,没写过 windows api
|
49
gam2046 362 天前
go error 的设计,好是好,但是 error 本质上是个字符串,这玩意不太好。如果可以包含一个类型信息就会好很多。
|
50
yazinnnn0 362 天前
因为现代语言大多可以用 monad 去处理这种问题 (比如 error, nil/null, future/promise)
时髦一点的语言甚至内建了 monad comprehension 去处理 monad 地狱的问题 但是 go 给的方案是 c 派的返回 errno 的方式, 也许 go 的哲学类似于 c, 但是 go boy 跳出来吹 err != nil 是令人困惑的 |
51
Ayanokouji 362 天前
@gitrebase 最外层打印日志也有点问题,如果调用链太深或者封装的 error 太多,还是不方便直接看出错误源,error 不自带调用栈,我觉得这是最难接受的
|
52
yazinnnn0 362 天前
rust 和 go 一样个毛....
rust kotlin fsharp swift 都从 ocaml 里抄了不少设计, dart 未来也会向 ocaml 方向靠 go 有一点 ocaml 的影子吗 |
53
otakustay 362 天前
go 这套显然和 shell 是一样的,有人喜欢 shell 的异常处理吗
|
54
qianzanqi 362 天前 via Android
@bwangel 如果 service 有两处调用 rpc 就还得依靠 message 内容来区分调用处了。
我一直觉得 go 处理异常要么无脑 WithStack 这个就相当于 java 的一句一 try ;要么无脑 WithMessage 传个随机字符串,查调用栈只要在项目里搜索就行,还没有运行时的性能问题 |
55
flyqie 362 天前 via Android
go 写业务是折磨,错误处理这边啥玩法都有。。
|
56
MegrezZhu 362 天前
要求函数迪调用方每次调用都显式地验证错误是否存在,又不提供语法糖才是问题的根源。
事实上我还挺喜欢这种 error pattern 的,在我司的 C++代码里面大量存在着 StatusOr<RetType>的用法,但它和 Go 不一样的是 C++有宏啊,绝大多数场景下我都不需要写`if (result.ok()) {}`,直接用一些`RETURN_IF_ERROR(func())`, `ASSIGN_OR_RETURN(auto ret, func())`就行了,没有什么重复性的代码要写…… |
57
me1onsoda 362 天前
增加了未知性,这是编程规范层面能解决的。禁止 catch exception runtimeexception 这种模糊的异常。但 go 就很影响编程体验。。
|
58
dacapoday 362 天前 1
@Ayanokouji 学艺不精,go 的 error 是接口,只是多数人 偷懒 或 习惯使用标准库的 字符串 实现。
想要调用栈完全可以自己加,相关的 error 库也非常多。 关键是 go 给了你选择,运行时收集调用栈是有开销的。 开发一些偏底层的库时,调用栈信息又完全不够,error 接口 意味着可以自定义 数据结构 只收集关键信息。 对于调用者,对 error 接口进行类型推断,又能很方便的滤出 是底层系统 error, 还是自定义 error struct ,还是中间各层业务封装的 字符串 error |
59
nothingistrue 362 天前
|
60
buffzty 362 天前
go 的错误处理跟 c 语言一模一样,没有一个人喷 c 但是喷 go 的挺多的
再者说了 c 和 go 都有 try catch 模式,c 里面用 goto ,go 一般用 errgroup 也有可以用 goto |
61
Huelse 362 天前
其实判断这个设计好不好很简单,看其他语言有没有类似的讨论即可。
像 rust 就不会有这类讨论,当然 rust 有其他的问题,但至少在错误处理上是没什么可争议的,反观 go 争议就特别多。 |
62
lujiaxing 362 天前 1
我就这么说:
在 Java 或者 ASP.NET Core 程序里, 你是可以做全局异常捕获的. 甚至如果底层框架做得好的话, 还可以把 throw Exception 作为中断请求的手段. 当请求数据不正常的情况下, 不需要你一层一层的从逻辑层 return 到展示层, 从子系统 A return 到子系统 B 然后再 return 到 api 层. 直接: ``` def getById(id): if id is None: raise BusinessException("ID 都不给我你问你马呢???"); ...... ...... ...... ``` 就可以了, 这句话不需要程序员任何的处理就可以很优雅的返回给 GUI 层. 如果是 go, 那就要一层一层的定义接收异常信息的参数. 然后像洋葱一样一层一层一层一层的去返回. 你跟我说这种设计非常好?????????? |
63
mightybruce 362 天前 1
@buffzty try catch 不是 goto 的封装,在 C 中实现 try catch 也不是一种方式,简单一点是 setjmp longjmp,
很多代码是从汇编的方式实现的。 try catch 这些和对象资源也是绑定的, 在 C 中实现的也是个模拟的语法糖,对象资源回收是不好实现的 Rust ,它里面也是满屏幕的.unwrap(),这实际上跟 Go 的 if err != nil 一样 写业务的还是太闲了, 天天讨论这些。 |
64
TiaoYeTaiLang 362 天前
因为 golang 一开始就不是为了开发业务系统用的,而是用于开发中间件系统这种偏底层应用的,对于底层应用来说 error 无非就那么几种,要不计算异常、要不 IO 异常。
|
65
cosiner 362 天前
错误是 error, 对应 java 的 checked exception, 异常是 panic, 对应 unchecked exception
go 的问题是 if err != nil 太频繁,因为大多数 go 程序都是 io 类型的,io 的 error 情况非常多,所以才会这样 换成 compute 类型的,就没那么多 error 了 go 就相当于 java 里面每一个 checked exception 都要手动 throw ,缺少一个语法糖 至于错误处理都是一样的,go 里面不要用 _ 忽略错误,java 不要 try catch {}不处理 |
66
jhdxr 362 天前
|
67
chenqh 362 天前 4
@MorJS 不是啊,你不打印堆栈的话,java 的错误看起来不就是和 golang 一样了吗?但是你愿意看这种错误吗?没有堆栈的 err
根本排查不了,golang 不就是这样,就凭一个 err 鬼知道到底是因为什么出错的,没有堆栈拿头排查 |
68
serialt 362 天前
写一下 shell 你就会习惯 go 的错误处理
|
69
bv 362 天前
掌握流量密码,撕比确实能招来一大堆回复。
|
70
pkoukk 362 天前
@Ayanokouji #8 try catch 和 error 不是一回事,try catch 对标的是 panic 。
|
71
neoblackcap 362 天前
@chenqh 也许他们都喜欢用 core dump 分析问题
|
72
chenqh 362 天前 1
@neoblackcap 可能用 golang 的都是大佬呗,不用堆栈,仅仅 print err 就可以知道到底是什么原因导致的错误,反正我不行
|
73
lesismal 362 天前 2
看名字就知道了,一个是错误处理,一个是异常处理。
go 用 panic 照样能像 java 那样处理异常,但本来就是为了处理错误所以用 if error 怎么了?而且哪里有错就现场处理、代码的可读性更好。只是那些其他语言习惯了用异常的人一直不这样搞才会觉得别扭。你看写 c 的人有几个说 go 错误处理垃圾的?即使 cpp ,也少有项目把错误处理都搞成 java 那种用异常处理的方式 反倒是 java 和一些其他语言,错误处理的场景和异常处理的场景,都用处理异常的方式来处理。习惯了自己的这种用异常处理去处理错误的方式,然后来鄙视 go 的该用啥用啥的鲁棒性,这帮人可真逗 |
74
lesismal 362 天前 2
go 这块的本质就是:
用错误处理的方式处理错误 用异常处理的方式处理异常 java 那些: 用异常处理的方式处理错误 用异常处理的方式处理异常 好奇你们哪里来的自信喷 go |
75
lxdlam 362 天前 3
单从两种范式上来说,值类型跟异常类型错误直接比较很难比较清楚:
- 值类型还是将错误当作值,本身没有控制流操作,所以当作值处理可以轻松当作比如 data 的一部分到处传递处理等等,缺点就是缺乏强制力,简单把值 ignore 掉就可以丢掉错误,而大家都倾向于懒一点; - 异常类型可以打断并接管控制流,更加强大,guarantee 也更好,如果 runtime 实现得不错(比如 Java )那用起来其实还是很舒服的,缺点就是这个特性很容易被误用(相信大家都听说过或者见过用 exception 传值的业务代码)。 两种范式各有优劣,其实没有直接关系,语言风格而已。 而单纯讨论值类型错误,Go 的实现有自己的优势。错误这个东西本身就是很容易产生很多 concrete type 的,基于 duck typing 的 interface 化,其实大大简化很多操作,比如直接把已有 error wrap 起来,然后实现一下 `Error` 和一些 helper function ,就能快速搞一个新的错误出来,开发效率是很高的(没有 thiserror 和 anyhow 之前,Rust 搞个自定义错误有多痛苦,相信很多朋友深有体会)。 Go 最大的问题其实在于,他采用了值类型错误,但是类型系统支持非常 basic ,导致大家处理错误非常痛苦。比如在类型系统比较现代的语言中,这种双类型语言或多或少能实现一定程度的 monad ,这样我们就可以写出类似于: ``` let val = some_function().map(fun).map(foo).map(bar); match val { Ok(v) => println!("Value is: {}", v), Error(e) => println!("Failed! error: {}", e), } ``` 在这里面,我们我们确实不关心中间错误,直接 chain 起来,处理最后错误就行,而如果我们关心比如到某一步的错误,我们单独接一下这一步的结果就可以拿到中间结果再做处理。无论是看起来还是读起来,其实都非常清晰明了。而可惜的是,Go 因为一开始设计压根没想这个问题,导致现在的打补丁的 generics ,很长线看起来都很难支持这种比较完备的 monad 特性。所以,我们只能一步步 if-else ,或者将这三个函数塞到一个 slice 里面,用 `reflect` 循环每个函数 apply 去每一步拿一下 type 信息做一些非常丑的补丁。 稍微补一句:为啥这里需要比较好的类型系统才能实现?在上面的流程中,实际上 `map` 接到的函数签名类型是各不一样的,有比较好类型系统的编译器在这里可以做非常好的静态推断;或者我们完全交给动态类型,损失一定 runtime 性能也能做到。而可惜的是,Go 既要选择尽量静态类型,又没把静态类型做很好,就卡在这了。 |
77
bthulu 362 天前 1
@lesismal java 也可以用处理错误的方式处理错误的. java 没限制你的返回类型, 你可以返回一个 result<err, data>, 然后再处理就是.
|
78
bronya0 362 天前
如果一个编程语言的设计真的很好的话,为什么会有这么多人吐槽?为什么 java 、python 基本不吐槽这个?
|
79
bronya0 362 天前
go 刚出的时候,这些都是人人吐槽的垃圾设计,过了很多年出了 k8s 和 docker 后,就开始有人舔了
|
80
buffzty 362 天前
@mightybruce linux 源码都是用 goto 做的 try catch 。setjump 也可以做 goto 比较简单
#include <stdio.h> typedef struct Ex { int code; char* msg; } _Ex; void exception() { _Ex* ex; // try { // xxx do something if (1) // throw { _Ex tmp_ex = {1, "xxx error"}; ex = &tmp_ex; goto _handle_ex; } // xxxx do something goto _no_ex; // } end try _handle_ex: // catch { ex != NULL && printf("Exception: %d, %s\n", ex->code, ex->msg); _no_ex: // } end catch } int main(int argc, char* argv[]) { exception(); return 0; } |
81
somebody1 362 天前 4
看完 13 楼的回答,我从眼睛到心灵都被震惊了!!!
“1. try catch 判断比字符串判断高级些吗?越简单明了的设计反而是好的设计。” 对,就是高级啊!!!! 类型判断就是比字符串判断高级啊,这还用说嘛!!!全是字符串判断要累死人啊,但是类型判断很简答啊,而且更符合对业务的代码表达!!!! 你写个一两百行的代码,当然字符串判断很简单。要是几万行呢,一个一个字符串判断???你的字符串手册都得搞个几百页吧!!! 购物业务的库存不足异常,请用 A 字符串,库存锁定请用 B 字符串。 订单的查询失败请用 C 字符串,无此订单请用 D 字符串。 安全策略失败请用 D 字符串,安全策略未知异常请用 E 字符串。 然后一直 2000 个字符串。 你真的写过代码吗?一定要回答我这句话!!! |
82
yannxia 362 天前
@lesismal 错误和异常也是主动区分出来的(无对错之分),对于 java 来说,只有 expection ,最多分 checked 和 unchecked 。Error 就是一个普通对象( GO 也是),这不过 Go 支持多返回值,大家就基于这个特性搞了这个用法,也可以自己封装个 Result 出来用。
说到底看场景, - 大多数 Java 开发者面对的是业务系统,系统中一旦出现了 error ,大多数情况下是需要中断整个链路的,而且因为有 GC ,不用考虑 RAII 的问题,直接原地回到顶结束就好了。所以这个成了惯用法。 - Go 的开发者在早期纯业务的很少,主要写一些 CNCF 那些中间件,考虑点和业务系统不一样,希望精细的掌控错误,现在这套就还也还行。 立场主要看写啥,我拿 go 写业务系统的时候,我也挺讨厌 error 的,99%的时候除了往上抛,又能做什么呢。但是 java 写中间件的时候,用 checkedexpection 体验也不是挺好。 |
83
xFrye 362 天前
go 有挺多不错的地方,但为什么就偏偏去吹 error 的设计呢
|
84
xausky 362 天前
@lujiaxing go 语言是让你有得选而 Java 很多时候没得选
比如 Go 想要实现你发的框架统一处理 ``` if err != nil { panic(err) } ``` 结合有人说 Must Helper 更简单 而有的东西比如就是可以处理的,Java 有时候就非要让你 try 比如 ``` Map<String, Object> result = new HashMap<>(); try { result = new ObjectMapper().readValue("{}", new TypeReference<HashMap<String, Object>>(){}); } catch (JsonProcessingException e) { // ignore } ``` 而 go ``` var result map[string]interface{} _ = json.Unmarshal([]byte("{}"), &result) ``` |
85
bronya0 362 天前
go 写业务就是一坨答辩,不用怀疑,可以认为没有错误机制,很原始,当成增强的 c 用就行了
|
86
thinkershare 362 天前
|
87
yazinnnn0 362 天前 2
https://fsharpforfunandprofit.com/rop/ Scott Wlaschin 做过 error handling 方面的 monad 科普 https://fsharpforfunandprofit.com/rop/rop427.jpg |
88
lujiaxing 362 天前
@xausky 问题人家 java 里出现异常是要求中断的. 无论如何后面都不能继续执行, 直接回到调用栈的最顶层. 我知道 go 可以全局 if err != nil. 问题是如果要求抛出异常之后中断后续全部逻辑, 阁下又当如何应对? 抛出的不是程序逻辑错误而是业务逻辑设计上的异常情况, 你不还是要一层一层的 if err != null 么?
|
89
wyx119911 362 天前
@thinkershare #43 C/C++起码还有宏啊,并不难用也不难看
|
90
lujiaxing 362 天前
|
91
ShadowPower 362 天前
|
93
lxdlam 362 天前
@ShadowPower 上面 #67 老哥也提到了,其实这个范式在函数式社区很常见,我们重点不关注你是不是所谓的“错误”,而关注类型本身,考虑用类型做 matcher 去配合值做 transformation 。这些年一些工业界的看起来很新的方法都来源于此,比如经典的 parser combinator ,如果你把 `Result<T>` 这个二元类型异构容器思考成 `Pair<&str /* remain input */, Result<T /* Token type */>>`,其实这里面的函数组合跟我上面说的 error monad 是一致的。更进一步来说,函数式的底层数学抽象 lambda calculus ,其实就是一种 combinatory logic ,天生以组合为主。在这种背景下,函数式语言采用这种思考是非常直接的。
|
94
bronya0 362 天前
Go 语言的 `error` 类型设计是一种简单而有效的方式来处理错误。它具有以下优点:
* **简单易用:** `error` 类型是一个内置类型,不需要任何额外的库或框架。它可以很容易地与其他语言的错误处理机制集成。 * **类型安全:** `error` 类型是一个接口类型,这意味着它可以表示任何实现了 `Error()` 方法的类型。这使得错误处理更加类型安全,因为编译器可以检查错误类型是否与预期的一致。 * **可扩展性:** `error` 类型可以很容易地扩展,以支持自定义错误类型。例如,我们可以定义一个 `MyError` 类型,它包含额外的错误信息或上下文。 然而,Go 语言的 `error` 类型设计也有一些缺点: * **缺乏详细的错误信息:** `error` 类型本身只包含一个错误消息。这使得调试错误变得困难,因为我们无法获得有关错误的更多信息。 * **没有错误代码:** `error` 类型没有提供错误代码。这使得在不同系统之间共享错误信息变得困难,因为每个系统可能使用不同的错误代码。 为了解决这些问题,我们可以使用一些第三方库或框架来增强 Go 语言的错误处理机制。例如,我们可以使用 `github.com/pkg/errors` 库来包装错误并添加额外的上下文信息。我们也可以使用 `github.com/go-kit/kit/log` 库来记录错误并添加额外的元数据。 总体而言,Go 语言的 `error` 类型设计是一种简单而有效的方式来处理错误。它具有简单易用、类型安全和可扩展性的优点。然而,它也有一些缺点,例如缺乏详细的错误信息和错误代码。我们可以使用一些第三方库或框架来增强 Go 语言的错误处理机制,以解决这些问题。 ## 改进建议 以下是一些改进 Go 语言错误处理机制的建议: * **在 `error` 类型中添加错误代码:** 这将使在不同系统之间共享错误信息变得更加容易。 * **提供一种标准的方式来记录错误:** 这将有助于确保错误信息以一致的方式记录下来,并包括所有相关元数据。 * **鼓励使用自定义错误类型:** 这将使错误信息更加具体和有意义。 * **提供一种标准的方式来包装错误:** 这将使我们可以轻松地将错误从一个系统传递到另一个系统,而不会丢失任何信息。 通过这些改进,我们可以使 Go 语言的错误处理机制更加强大和灵活。 |
96
bronya0 362 天前 1
Go 和 Java 两种语言的错误处理机制都有各自的优势和适用场景,而哪个更优秀主要取决于具体的需求和开发者的偏好。以下是对两者的简要比较:
### Go 的错误处理机制: 1. **优势:** - **简洁和清晰:** Go 使用多返回值和接口类型的错误,代码看起来简洁而清晰,错误处理直接与函数调用关联。 - **显式处理:** Go 鼓励显式地处理错误,开发者需要明确检查并处理每一个错误。 - **错误值为接口:** 错误是一个接口类型,允许开发者自定义错误类型,提供更多的上下文信息。 2. **劣势:** - **容易忽略错误:** 由于错误是常规的返回值,存在一定的可能性开发者会忽略错误。 - **容易忽略错误:** 由于错误是常规的返回值,存在一定的可能性开发者会忽略错误。 ### Java 的异常处理机制: 1. **优势:** - **异常层次结构:** Java 引入了异常层次结构,允许开发者根据具体的异常类型进行处理,使得代码结构更加灵活。 - **支持异常链:** 异常可以包含其他异常,形成异常链,提供更多上下文信息。 - **Checked 和 Unchecked 异常:** Java 区分 Checked 和 Unchecked 异常,使得开发者能够有选择地捕获和处理异常。 2. **劣势:** - **语法冗余:** Java 的异常处理语法相对冗长,有时需要使用大量的 try-catch 块。 ### 总体评价: - **Go 适用于简单和清晰的代码结构,强调显式错误处理。适用于构建轻量级服务和工具。** - **Java 适用于大型企业级应用,具备更复杂的异常处理机制,支持更精细的错误处理。** 在选择错误处理机制时,可以根据具体的项目需求和团队经验做出权衡。部分团队可能更喜欢 Go 的简洁性和明确性,而部分团队可能更倾向于 Java 提供的异常层次结构和更灵活的处理方式。 |
97
allanpk716 362 天前 via iPhone 1
看了几个吐槽 go err 问题的,这玩意是简陋,但是你们好歹多用下,或者去看看最佳实践嘛…
什么 err 是判断字符串的都出来了,那是你不用 errors.is 的问题吧 喜欢用 go ,就是喜欢死板的处理所有的 err ,培训新人友好,你就把能处理的都处理就好了,从头培养编码要考虑鲁棒性的观念。 非说 try catch 包裹代码优雅,没错,编码能力好的,确实优雅,看的舒服。问题是在我看到的项目中,绝大多数用法就是嫌弃的一比啊…越是水平一般的,越喜欢滥用 try catch (只代表我看到的项目和人),反正该判断的返回值都不管了,try catch 万能… |
98
Masoud2023 362 天前
因为你 Java 模板代码写多了,所以你才觉得这种糟透了的模板代码没问题。
你说“但是增加了未知性。”我只能默认你是不了解 Java 的工作细节。 |
99
harry890829 362 天前
@allanpk716 我也是看了看,感觉真正用的深的没几个,errors.is 这个我必须吐槽一下,这里有个 is ,但是要写入的时候一定要 fmt.Sprintf("%w",err),这个文档和 errors 包的文档不在一起,导致很多人是不知道,或者找不到的
|
100
adoal 362 天前
golang 里的错误处理,从意图上来看,似乎跟某些语言里用 left/right 、result/error 的带值枚举类型来处理错误有相同的动机。但是人家用的是“和”类型,而它居然用的是“积”类型。
|