Go 语言的“老大难”:错误处理为何总是被吐槽?

B站影视 日本电影 2025-06-05 07:41 2

摘要:func printSum(a, b string) error { x, err := strconv.Atoi(a) if err != nil { return err } y, err := strconv.Atoi(b) if err != nil

作为一门简洁高效的编程语言,Go 语言深受开发者喜爱,但有一个问题多年来始终被社区诟病:错误处理的冗长。在 Go 代码中,类似下面的模式随处可见:

x, err := callif err != nil { // 处理错误}

这种 if err != nil 的写法在代码中反复出现,特别是在涉及大量 API 调用的程序中,错误处理代码甚至会“淹没”核心逻辑。来看一个例子:

func printSum(a, b string) error { x, err := strconv.Atoi(a) if err != nil { return err } y, err := strconv.Atoi(b) if err != nil { return err } fmt.Println("result:", x + y) return nil}

这个函数有 10 行代码,但只有 4 行在干“正事”,其余 6 行都是错误处理的“噪音”。这种冗长让开发者抓狂,也让“错误处理”连续多年登顶 Go 年度用户调研的吐槽榜。(一度被泛型问题盖过风头,但自从 Go 支持泛型后,错误处理又成了头号槽点!)

Go 团队的多年努力

Go 团队非常重视社区反馈,早在 2018 年就启动了“Go 2”计划,试图解决这个问题。当时,Russ Cox 提出了基于 check/handle 机制的草案,由 Marcel van Lohuizen 设计,试图让代码更简洁:

func printSum(a, b string) error { handle err { return err } x := check strconv.Atoi(a) y := check strconv.Atoi(b) fmt.Println("result:", x + y) return nil}

这个方案虽然优雅,但被认为过于复杂。2019 年,Go 团队推出了更简单的 try 提案,将 check 改为内置函数 try,省略了 handle 部分:

func printSum(a, b string) error { x := try(strconv.Atoi(a)) y := try(strconv.Atoi(b)) fmt.Println("result:", x + y) return nil}

然而,try 提案因隐藏控制流(错误时直接返回)引发争议,GitHub 讨论区涌入近 900 条评论,最终被放弃。2024 年,Ian Lance Taylor 又借鉴 Rust 的 ? 运算符,提出新方案:

func printSum(a, b string) error { x := strconv.Atoi(a) ? y := strconv.Atoi(b) ? fmt.Println("result:", x + y) return nil}

可惜,这个方案依然未获广泛支持,评论区充斥着各种微调建议,讨论最终无疾而终。

为何解决不了?

Go 团队和社区提出了数百种错误处理方案,但没有一种能达成共识。原因何在?

现状并非不可接受:Go 的错误处理虽然冗长,但逻辑清晰,易于调试。15 年过去了,这种风格已成为 Go 的“标志”,贸然改变可能让代码显得“不地道”。新语法成本高:语言变更不仅要改编译器,还要更新文档、工具和现有代码,成本巨大。社区意见不一:有人觉得冗长是问题,有人认为习惯后无所谓。Google Cloud Next 2025 的 Go Meetup 上,许多用户明确表示不需要新语法。工具可缓解问题:现代 IDE 的代码补全功能让写错误处理变得轻松,未来或许还能通过“隐藏错误代码”的设置改善阅读体验。

Go 团队的决定:暂停语法变更

经过多年尝试,Go 团队宣布:未来一段时间将停止为错误处理引入新的语法,并关闭相关提案。原因很简单:没有哪种方案能获得社区的广泛支持。Go 团队认为,与其在语法上纠缠,不如聚焦其他改进,比如通过标准库减少样板代码。例如,使用 cmp.Or 可以简化错误处理:

func printSum(a, b string) error { x, err1 := strconv.Atoi(a) y, err2 := strconv.Atoi(b) if err := cmp.Or(err1, err2); err != nil { return err } fmt.Println("result:", x + y) return nil}

此外,Go 提倡“错误即值”的理念,鼓励开发者为错误添加上下文信息,比如:

if err != nil { return fmt.Errorf("invalid integer: %q", a)}

这种方式虽然仍显冗长,但能提供更有用的错误信息,实际项目中更常见。

未来展望

虽然语法变更暂时搁置,但 Go 社区的热情从未消退。多年来,社区贡献了无数提案,催生了许多其他方面的改进。或许未来某天,新的灵感会带来更好的解决方案。但现在,Go 团队希望将这份热情投入到其他领域,让 Go 变得更强大。

你怎么看?
Go 的错误处理真的需要大改吗?还是“冗长但可靠”就是它的魅力?欢迎留言分享你的看法!

参考链接

Go 错误处理提案汇总:https://github.com/golang/go/issuesGo 开发者调研 2024:https://go.dev/surveyRuss Cox 博客:https://research.swtch.com

来源:SuperOps

相关推荐