V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yujianwjj
V2EX  ›  Go 编程语言

go 关于函数返回 error 的一个疑问

  •  
  •   yujianwjj · 170 天前 · 2021 次点击
    这是一个创建于 170 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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)
    	}
    }
    
    23 条回复    2024-11-25 23:34:05 +08:00
    mainjzb
        1
    mainjzb  
       170 天前   ❤️ 2
    。。如果每次都要校验 value ,为什么要设计 error

    rust 和 zig 这样的语言只有两种形式,value nil 或者 nil error
    而 golang 多一种 value error 的形式 在 io.write 上很常用,代表写到一半的时候出错了。可以知道已经写了多少。
    而你,试图引入第四种,nil nil 形式。
    很难评这是一个工作了很多年的程序员。
    zzhaolei
        2
    zzhaolei  
       170 天前
    这就是约定,err == nil ,默认另一个值就是正常的,err != nil ,则另一个值就返回对应的零值。

    err == nil ,为什么另一个值你不返回正常的呢?这设计本身也是不对的。没有异常不应该说明函数的流程是正常的?流程是正常的为什么结果是错误的?
    kdwnil
        3
    kdwnil  
       170 天前 via Android
    err 不等于 nil ,那一定有问题,先处理(返回),函数后续如何就不需要再关心这个 err 了;不等于 nil 不代表其他返回值就没问题,所以后续也还要继续判断返回值合不合法。这样做虽然会有点罗嗦,但很干净

    如果处理 err 放在后面,像 op 示例这种单个的还好,但如果我有一个复杂的函数,嵌套了很多层这种判断,我是不是应该先开发一个斜 45°的滚动条?
    kdwnil
        4
    kdwnil  
       170 天前 via Android
    处理错误是很灵活的,要根据实际情况来决定先处理等于还是先处理不等于
    deplives
        5
    deplives  
       170 天前
    err != nil 表示 result 是有效的值,按理说至少也应该返回一个 A 的空对象而不是 nil
    GuuJiang
        6
    GuuJiang  
       170 天前 via iPhone   ❤️ 4
    恭喜你发现了 go 的一个致命伤,例如 Haskell 中的 Either 、rust 和 swift 中的 Result 等等基于 Monad 的返回类型,都应该是一个 sum type ,然而 go 却定义了一个 product type
    leonshaw
        7
    leonshaw  
       170 天前
    0 值未必不是正常值
    lolizeppelin
        8
    lolizeppelin  
       169 天前
    脱离业务实际逻辑讨论是错误的

    一个简单的例子
    本地查询缓存与数据库查询 map[int]*value

    当本地查询不到结果的时候,去数据库查询,也没有结果
    这时候应该缓存一个 nil,避免不匹配时,击穿缓存

    这时候查询结果和错误都是 nil
    Felldeadbird
        9
    Felldeadbird  
       169 天前
    有意思,感觉是代码设计问题,a 的返回值应该是明确的。直接返回 nil ,算是写一个 BUG 了。

    刚才翻看了自己写的 go 代码,确实有类似的现象。也是 erro != nil 。 不过其他类型返回都是正常值的,不会直接返回不明确的。 应该不会出现楼主遇到的现象。
    w568w
        10
    w568w  
       169 天前   ❤️ 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 就能搞定的简单任务罢了。
    wangritian
        11
    wangritian  
       169 天前
    不用担心,一般开源项目不会犯低级错误,err 为空则 a 一定是非空
    但确实防不住一些大聪明同事,当然也有少数需要 a=nil 的情况,一般更建议加一个 bool
    afxcn
        12
    afxcn  
       169 天前
    return nil, nil 这种写法是自己给自己找麻烦。

    自找麻烦的话,谁也帮不了你。
    Ayanokouji
        13
    Ayanokouji  
       169 天前
    我的常规写法
    rerord,err:=get()
    switch {
    case err == nil:
    do something
    case errors.is(err, NotFoundErr):
    do something
    default:
    return nil, err
    }
    rrfeng
        14
    rrfeng  
       169 天前
    所以 go 官方 http 的 resp,err := http.Do(xx)
    还要特别说明 err 情况下不会有 resp (避免资源泄露)
    rimutuyuan
        15
    rimutuyuan  
       169 天前
    99%情况下 a 一定不为 nil

    我在 db 查询没有记录时,习惯将 error = record not found 自己处理一下,返回 nil,nil
    CoderXI
        16
    CoderXI  
       169 天前
    是一个好问题
    1. 非必需不建议使用指针, a.a 永远不会 panic, 但是你仍然需要类似指针检查的判断 0 值。
    2. 让 0 值有意义
    3. err == nil 大多不应该让 a 没有意义
    pkoukk
        17
    pkoukk  
       169 天前
    你的这个 f() 应该是个初始化函数,一般会取名叫 NewA() (*A, error)
    这个 NewA ,正常人都不会觉得,err==nil 的时候,还能初始化一个空值来
    meowrain
        18
    meowrain  
       169 天前
    有人会写 return nil,nil 吗。。。。
    ragnaroks
        19
    ragnaroks  
       169 天前
    这应该属于编辑器代码检查的功能,就像 typescript 的 type = {hasError:true, data:null} | {hasError:false, data:TheDataType} 可以让编辑器提示用户不要写出 return {true,null}
    eddievim
        20
    eddievim  
       169 天前
    防御性编程,对于指针类型的返回值,额外加判空逻辑
    eddievim
        21
    eddievim  
       169 天前
    你的场景应该是只在乎返回值是不是 nil, 那可以 a, _ := f(),对错误进行忽略即可
    body007
        22
    body007  
       169 天前
    我之前也有过疑问,但我尽量规范自己的代码,当 err != nil 时其他返回值数据必须正确,如果有特殊情况,我会返回特殊 err ,在 err != nil 代码块内部使用 errors.Is(err, 特殊错误),我感觉这样代码更清晰易懂。

    直到我需要用到标准库的这个字段时,翻看源代码发现有 nil,nil 的场景。



    于是我的代码就返回了不规范的 nil,nil 了



    所以 Go 这个没有强制规范略微蛋疼,即使规范了,标准库也在违反规范。我们可以善用 golangci-lint 检查这些存疑的代码吧。
    BKH3h4F
        23
    BKH3h4F  
       29 天前
    防御性检测(
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5681 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 06:26 · PVG 14:26 · LAX 22:26 · JFK 01:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.