type A struct {
a int
}
func f() (*A, error) {
// do something
// if err != nil {
// return nil, err
// }
return nil, nil
}
func TestX(t *testing.T) {
a, err := f()
if err != nil {
t.Log(err)
} else {
t.Log(a.a) // 这里 a 为 nil ,所以 a.a 非法访问,崩溃
}
}
go 关于错误处理,似乎默认都是大家约定,如果 err != nil ,另外一个值是正常的值,但是理论上来说,即使 err = nil ,另外一个值也不一定是合理的值。感觉下面的写法似乎更好一点。
func TestX(t *testing.T) {
a, err := f()
if a == nil {
t.Log(err)
} else {
t.Log(a.a)
}
}
1
mainjzb 171 天前 2
。。如果每次都要校验 value ,为什么要设计 error
rust 和 zig 这样的语言只有两种形式,value nil 或者 nil error 而 golang 多一种 value error 的形式 在 io.write 上很常用,代表写到一半的时候出错了。可以知道已经写了多少。 而你,试图引入第四种,nil nil 形式。 很难评这是一个工作了很多年的程序员。 |
2
zzhaolei 171 天前
这就是约定,err == nil ,默认另一个值就是正常的,err != nil ,则另一个值就返回对应的零值。
err == nil ,为什么另一个值你不返回正常的呢?这设计本身也是不对的。没有异常不应该说明函数的流程是正常的?流程是正常的为什么结果是错误的? |
3
kdwnil 171 天前 via Android
err 不等于 nil ,那一定有问题,先处理(返回),函数后续如何就不需要再关心这个 err 了;不等于 nil 不代表其他返回值就没问题,所以后续也还要继续判断返回值合不合法。这样做虽然会有点罗嗦,但很干净
如果处理 err 放在后面,像 op 示例这种单个的还好,但如果我有一个复杂的函数,嵌套了很多层这种判断,我是不是应该先开发一个斜 45°的滚动条? |
4
kdwnil 171 天前 via Android
处理错误是很灵活的,要根据实际情况来决定先处理等于还是先处理不等于
|
5
deplives 171 天前
err != nil 表示 result 是有效的值,按理说至少也应该返回一个 A 的空对象而不是 nil
|
6
GuuJiang 171 天前 via iPhone 4
恭喜你发现了 go 的一个致命伤,例如 Haskell 中的 Either 、rust 和 swift 中的 Result 等等基于 Monad 的返回类型,都应该是一个 sum type ,然而 go 却定义了一个 product type
|
7
leonshaw 171 天前
0 值未必不是正常值
|
8
lolizeppelin 171 天前
脱离业务实际逻辑讨论是错误的
一个简单的例子 本地查询缓存与数据库查询 map[int]*value 当本地查询不到结果的时候,去数据库查询,也没有结果 这时候应该缓存一个 nil,避免不匹配时,击穿缓存 这时候查询结果和错误都是 nil |
9
Felldeadbird 171 天前
有意思,感觉是代码设计问题,a 的返回值应该是明确的。直接返回 nil ,算是写一个 BUG 了。
刚才翻看了自己写的 go 代码,确实有类似的现象。也是 erro != nil 。 不过其他类型返回都是正常值的,不会直接返回不明确的。 应该不会出现楼主遇到的现象。 |
10
w568w 171 天前 2
从 Type Theory 来说结论早就有了:Go 的 Prod type 抽象就是错的。
在错误的抽象前提下,不管讨论什么都是错的。我就不掺和楼主对具体情况的讨论了。 正确的抽象如 Rust 、Zig 、Haskell 都是 Sum type ,即要么 Value ,要么 Error 。 ==== 先打预防针:我猜肯定会有人 Argue 说「难道你不考虑既有错误又有返回值的情况吗?」,拜托,那应该是 Value | Error | (Value, Error) 这个复合类型,怎么也轮不到 (Value, Error) 吧。即使 (Value, Error) 确实从结果来看「巧合」地达成了复合类型的结果,后续处理中也依然要分成 value != nil && error ==nil, value == nil && error != nil, value != nil && error != nil 三种情况来处理。发现了吗?只不过是在重新发明 Sum type 用一个 pattern matching 就能搞定的简单任务罢了。 |
11
wangritian 171 天前
不用担心,一般开源项目不会犯低级错误,err 为空则 a 一定是非空
但确实防不住一些大聪明同事,当然也有少数需要 a=nil 的情况,一般更建议加一个 bool |
12
afxcn 171 天前
return nil, nil 这种写法是自己给自己找麻烦。
自找麻烦的话,谁也帮不了你。 |
13
Ayanokouji 171 天前
我的常规写法
rerord,err:=get() switch { case err == nil: do something case errors.is(err, NotFoundErr): do something default: return nil, err } |
15
rimutuyuan 171 天前
99%情况下 a 一定不为 nil
我在 db 查询没有记录时,习惯将 error = record not found 自己处理一下,返回 nil,nil |
16
CoderXI 171 天前
是一个好问题
1. 非必需不建议使用指针, a.a 永远不会 panic, 但是你仍然需要类似指针检查的判断 0 值。 2. 让 0 值有意义 3. err == nil 大多不应该让 a 没有意义 |
17
pkoukk 171 天前
你的这个 f() 应该是个初始化函数,一般会取名叫 NewA() (*A, error)
这个 NewA ,正常人都不会觉得,err==nil 的时候,还能初始化一个空值来 |
18
meowrain 171 天前
有人会写 return nil,nil 吗。。。。
|
19
ragnaroks 171 天前
这应该属于编辑器代码检查的功能,就像 typescript 的 type = {hasError:true, data:null} | {hasError:false, data:TheDataType} 可以让编辑器提示用户不要写出 return {true,null}
|
20
eddievim 171 天前
防御性编程,对于指针类型的返回值,额外加判空逻辑
|
21
eddievim 171 天前
你的场景应该是只在乎返回值是不是 nil, 那可以 a, _ := f(),对错误进行忽略即可
|
22
body007 171 天前
我之前也有过疑问,但我尽量规范自己的代码,当 err != nil 时其他返回值数据必须正确,如果有特殊情况,我会返回特殊 err ,在 err != nil 代码块内部使用 errors.Is(err, 特殊错误),我感觉这样代码更清晰易懂。
直到我需要用到标准库的这个字段时,翻看源代码发现有 nil,nil 的场景。 于是我的代码就返回了不规范的 nil,nil 了 所以 Go 这个没有强制规范略微蛋疼,即使规范了,标准库也在违反规范。我们可以善用 golangci-lint 检查这些存疑的代码吧。 |
23
BKH3h4F 31 天前
防御性检测(
|