写 go 语言的一种不作不会死的方式

2022-07-22 22:05:41 +08:00
 join

不捕获 error

典型的像如下代码:

func getBar()(*bar, err){
	//some errors
    if err := hasError(); err != nil{
    	return nil, err
    }
	//do sth
    return new(bar), nil
}
func foo(){
	b, _ := getBar()
    b.DoSth() //panic
}

以下典型的 golang 代码看起来很烦,而且直接影响代码风格美观程度。

func getBar()(*bar, err){
	//do sth
    return 0, nil
}

func foo()error{
	if b1, err := getBar(); err != nil{
    	return err
    }
    if b2, err := getBar(); err != nil{
    	return err
    }
    return nil
}	

我在很多项目里看到新手或者从其他语言转过来的同学,喜欢写 if p, _ := getBar() 这样的代码,然后继续处理接下来的流程。这样做的问题在于你直接忽略掉了你所调用代码产生的错误,你内心默认的代码运行方式是:“我的代码一定会以正确的方式运行。
在很多其他的语言里会有 try catch 这样的机制,可以让你在函数调用的外部直接捕获异常。当随之带来的问题是 try catch 是跨函数跨模块和函数的,容易出现抛出的异常被并不适合这个模块的代码模块去处理。
go 语言这个错误处理方式虽然丑陋,但简单有效。它这样做简单的让你把 error 直接丢给上一层,上一层可以决定这个错误是继续传递还是中止处理。当我把写 go 关于错误处理的思维方式转变成:不放过任何一个错误后,写代码的心智负担将得极低。我只需要关心当前的函数输入输出数据是否合法,以及调用其他函数时出错后选择继续还是中止。而且 go 语言这种小粒度的 error 处理方式几乎不用 care 错误的类型,遇到不合法 /异常 /错误 /非正常流程后只需要 error 继续 /中止一把梭即可。
不信你可以看看下面是如何用简单的错误处理是如何改掉 HTTP 200 一把梭的毛病。

func Query(w http.ResponseWriter, r *http.Request) {
	var data RequestData
    err := json.NewDecoder(r.Body).Decode(&data)
    if err != nil{
    	w.WriteHeader( http.StatusBadRequest)
    	return
    }
    if data.Name == "not existed"{
    	w.WriteHeader( http.StatusNotFound)
        return
    }
    w.WriteHeader( http.StatusOK)
    return
}

某些 golang 的 web framework 会调用 recover 方法,以防止 HTTP server 崩溃。不得不说这是它提供的一种“助纣为虐”的方式让你去忽略掉某些致命的错误。这完全没必要,守护进程比 recover 要靠谱得多(还能自动获得重启大法的加成:) )。我个人认为在代码里面尽量不要调用任何 recover 的代码,尽量在代码部署到生产环境前测试发现所有可能的 panic 错误并修复掉。如果代码发生 panic 问题,肯定是有什么地方你没有想到或没设计好,你需要做的是修改代码而不是掩盖错误。

捕获所有错误你唯一需要付出的代价就是:多敲几个字。而付出的这点代价你将获得:底层成本的设计负担,无脑的心智负担,健壮的代码,严格的输入输出限定。

3165 次点击
所在节点    Go 编程语言
12 条回复
kalista
2022-07-22 22:11:17 +08:00
用不用 recover 还是得看场景,对于开发来说,当然希望 panic 被我们感知到比较好,但是有些 to B 产品,甲方是会关注你是否有 panic 的,对于这种,我们需要做到既不 panic (不被甲方知道)同时我们又能知道服务出现过问题,总之就是具体事情具体分析。永远不能脱离业务
lysS
2022-07-22 22:15:13 +08:00
我是这样安慰自己的:总比 C 返回一个 err code 好吧。。。。
Ricardo5
2022-07-22 23:34:07 +08:00
主要还是太丑了,rust 用模式匹配看起来就美观很多
messyidea
2022-07-23 05:22:17 +08:00
panic 了整个进程就退出了,web framework 的所有其它并发请求都会失败,而如果 recover ,则不会影响其它并发请求。再说发现程序是否有 panic ,不一定要进程退出,panic 打点 metrics 告警也能发现问题
Aloento
2022-07-23 06:10:43 +08:00
总之就是太丑而且说真的麻烦,错误处理还得看 C#(你甚至都不知道它到底会不会报错)
dcoder
2022-07-23 07:48:55 +08:00
@Aloento
"错误处理还得看 C#(你甚至都不知道它到底会不会报错)"
LOL 这是黑还是夸 C# ?
tqyq88
2022-07-23 08:16:07 +08:00
判断 err != nil 才是恶心的根源
lidage
2022-07-23 15:18:39 +08:00
你捕获了又能怎么样呢,你能写出绝对完美的程序?自欺欺人,不如直接忽略掉,在重要的地方捕获 err 或者打日志,只有妥协,才能走的更快
mmdsun
2022-07-24 01:15:22 +08:00
@dcoder 可能说的是 C#异常不像 Java 那样必须 try catch 捕获,不然编译不过? 反正 Java 检查性异常满屏的 try catch ,看起来也十分不雅,我会用 lombok 插件来处理这种情况。
Aloento
2022-07-24 01:18:30 +08:00
@dcoder 只是在说对 C#的无奈
很多情况下如果写库的人不在注释里面标注要报什么错,我都不知道要不要 try 它
只有真出了问题才知道要 try
iosyyy
2022-07-24 09:23:04 +08:00
@mmdsun java 很多时候套个 aop 就直接抛就行了..统一做处理就可以了
buffzty
2022-07-24 12:46:37 +08:00
绝大多数觉得 go 语言错误处理不好的都是菜逼. 因为 go 的错误处理跟 c 语言是一样的,而且比 c 语言好.
并且 go 是有 try catch 模式的(不是 recover 那种) 每次看见一群菜鸟喷 err 错误处理就想笑 咋没人喷 c 的错误处理?

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

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

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

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

© 2021 V2EX