前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Go 中的错误处理:新的?运算符

Go 中的错误处理:新的?运算符

作者头像
用户11547645
发布2025-03-07 16:05:49
发布2025-03-07 16:05:49
2500
代码可运行
举报
文章被收录于专栏:萝卜要加油萝卜要加油
运行总次数:0
代码可运行

背景

错误处理一直是编程中的重要组成部分, Go语言因为它独特的错误处理模式饱受争议,任何一篇写如何讨厌Go语言的博客中,一定会把“繁琐的错误处理”放在靠前的位置。这个问题在 Go 社区引发了大量讨论,探讨如何在保持清晰性和可维护性的同时减少模板代码。

Proposal 详情

ianlancetaylor提出了一个新的提案#71203 ,在 Go 中引入用于错误处理的操作符?。用来简化Go的错误处理。后续Go的错误处理可能会变成这个样子:

代码语言:javascript
代码运行次数:0
复制
// now
result, err := someFunction()
if err != nil {
    return nil, err
}
// proposal ? 
result := someFunction()?

在本例中,两种写法的结果是相等的:如果 someFunction()返回错误,就返回。 这个proposal的 核心内容就是这样了, 主要目的是减少templ代码,同时保持 Go 的显式和简洁理念。是一个语法糖,在返回多个值的函数调用(例如 (T, error))之后使用时,它会自动检查最后一个值是否为非零(表示错误)。编译器将为 这种写法生成跟以前一样的代码,保证兼容性。 在正式提案中,ianlancetaylor详细阐述了?的语法规则:

  • ?只能出现在赋值语句或表达式语句的末尾,并且表达式必须要有返回值
  • 对于表达式语句,?“吸收”的是表达式的最后一个值(通常是err)
  • 对于赋值语句,?“吸收”的是右侧表达式的最后一个值(通常是err),这样右侧值的数量会比左侧变量的数量多一个。
  • 这个被“吸收”的值称为qvalue, 必须是实现了error接口的接口类型。
  • ?后面可以跟一个代码块。如果没有代码块,当qvalue不为nil时,函数会立即返回,并将qvalue赋给最后一个返回值。
  • 如果?后面有代码块,当qvalue不为nil时,代码块会被执行。在代码块中,会隐式声明一个名为err的变量,其值和类型与qvalue相同。

基本的使用场景可能是这样子的:

代码语言:javascript
代码运行次数:0
复制

r := os.Open("file.txt") ? // ? 吸收了  os.Open 的error, 如果不为空,就会返回。

funcRun()error { 
    Start() ? // 如果 Start 返回非 nil 的 error,立即返回该 error 
returnnil
}

funcprocess()error {
    result := doSomething() ? {
return fmt.Errorf("something failed: %v", err)  // qvalue 
    }
    anotherResult := doAnotherThing(result)?
returnnil
}

优点

这个 proposal 最重要(也是唯一的好处)好处是减少 Go 程序中的重复代码数量, 根据proposal 中的描述.

reduces the error handling boilerplate from 9 tokens to 5, 24 non-whitespace characters to 12, and 3 boilerplate lines to 2.

跟以前的错误处理提案try 等不同的是, ? 不会引入隐藏的控制流, ?的存在明确地指示了错误处理的逻辑。

缺点

最大的缺点就是所有的Go图书、资料需要更新,并且对于新人来说,可能需要理解这个概念,因为它跟其他语言的实现都不太一样。并且这个改动,会涉及很多代码,包括go src,所以Go Core Team 的压力也很大,因为机会只有一次。

Err 是隐式变量

?后面的代码块会隐式声明一个err变量,这可能会导致变量shadowing的问题。 proposal 中提到了一个例子:

代码语言:javascript
代码运行次数:0
复制
for n = 1; !utf8.FullRune(r.buf[:n]); n++ {
    r.buf[n], err = r.readByte()
if err != nil {
if err == io.EOF {
            err = nil// must change outer err
break
        }
return
    }
}
// code that later returns err

在这个例子中,赋值 err = nil 必须改变存在于 for 循环之外的 err 变量。如果使用 ? 操作符,就会引入一个新的 err 变量,遮蔽外部变量。 在本例中,使用 ? 操作符还会导致编译器错误,因为赋值 err = nil 会设置一个从未使用过的变量。

写代码的心智负担会增加

代码语言:javascript
代码运行次数:0
复制
func F1()error {
    err := G1()
    log.Print(err)
    G2() ?
    {
        log.Print(err)
    }
returnnil
}

funcF2()error {
    err := G1()
    log.Print(err)
    G2() ? {
        log.Print(err)
    }
returnnil
}

在这个例子中,这两个函数都合法,只有G2的换行符有差异,但它们的行为却完全不同。 这个差异可不能通过fmt等方式找补回来。

不改变的合理性

尽管Go的错误处理机制经常受到批评,但它仍然是可用的。因此,社区需要权衡是否真的需要进行改变。在proposal中,ianlancetaylor反复提到: "Perhaps no change is better than this change. Perhaps no change is better than any change"。这也一定程度上反映出Go Core Team在错误处理改进方面其实并不那么坚定,感觉更多是迫于Go社区的舆论和压力。

泛型: 别Q我

总结

新的proposal可以看出Go Core Team 还是在听社区的声音。?操作符提案为Go语言的错误处理机制提供了一种新的思路。该提案通过引入简洁的语法,可以显著减少错误处理的代码量,并使代码的主流程更加清晰。尽管现在还存在一些分歧,但是总算有人在推动不是?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萝卜要加油 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • Proposal 详情
  • 优点
  • 缺点
    • Err 是隐式变量
    • 写代码的心智负担会增加
    • 不改变的合理性
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档