乐趣区

关于go:Go-try-新提案靠谱吗想简化错误处理了

大家好,我是煎鱼。

在近日新的 try 提案《proposal: Go 2: error handling: try statement with handler》在社区引发了热议。

明天煎鱼和大家一起关上来看看,这能把 Go 错误处理机制给掀开重整不。

背景

来自 PingCAP 的提案作者 @Greg Weber 会干这事基于两个因素,一个是在《Go Developer Survey 2022 Q2 Results》中明确提到。

随着 Go1.18 泛型的公布,原先矛盾最深的泛型曾经失去一个初步的解决方案。在社区调研上,开发者在应用 Go 时面临的最大挑战曾经转移到了错误处理上,须要投入精力去“解决”它。

另外一个因素就是众所皆知的,Go 错误处理代码比拟繁琐,常被工程师们戏称一个 Go 工程里有 30% 都 if err = nil。

如下代码:

_, err := f()
if err != nil {...}
_, err = r()
if err != nil {...}
_, err = w()
if err != nil {...}

心愿让其更优雅。也有许多小伙伴认同这个设计,的确是简略、直观的解决,在社区造成了角力。

try-handler 提案

本次提案中所提到的解决方案,是减少一个新语句 try,以此达到简洁的错误处理的作用,让 if err != nil 的解决丝滑起来。

如下代码:

try err, handler

编译器翻译后生成的代码:

if err != nil {return handler(err)
}

在函数中能够如下:

func(args...) (rtype1, rtypes..., rtypeN, error) {
    try err, handler
    ...
}

翻译后生成的代码:

func(args...) (rtype1, rtypes..., rtypeN, error) {
    if err != nil {return Zero(rtype1), Zeros(rtypes...)..., Zero(rtypeN), handler(err)
    }
    ...
}

也能够只针对 if err != nil 解决。如下代码:

try err

翻译后生成的代码:

if err != nil {return err}

不会调用不存在的 handler 进行解决,将会间接返回。三行(if err != nil 的逻辑)间接变 3 个单词(try)。

不想写函数,也能够间接:

x, err := f()
try err, fmt.Errorf("f fail: %w", err)

针对 defer+try 的场景能够如下:

func CopyFile(src, dst string) error {defer try func(err error) error {return fmt.Errorf("copy %s %s: %w", src, dst, err)
    }
    ...
}

入参是比拟灵便的,作者心愿它是泛型,这样可能适配各种场景的要求。

示例和实际

针对本提案,原作者给出了各类应用场景的示例。如下代码:

import ("fmt")

// This helper should be defined in the fmt package
func Handlew(format string, args ...any) func(error) error {return func(err error) error {args = append(args, err)
        return fmt.Errorf(format+": %w", args...)
    }
}

// This helper should be defined in the fmt package
func Handlef(format string, args ...any) func(error) error {return func(err error) error {args = append(args, err)
        return fmt.Errorf(format+": %v", args...)
    }
}

func valAndError() (int, error) {return 1, fmt.Errorf("make error")
}

func newGo() (int, error) {x, err := valAndError()
    try err

    // Common formatting functions will already be provided
    i := 2
    x, err = valAndError()
    try err, Handlew("custom Error %d", i)

    // Using a custom error type
    // For convenience the error type can expose a method to set the error
    x, err = valAndError()
    try err, TheErrorAsHandler(i)
}

type TheError struct{
    num int
    err error
}

func (t TheError) Error() String {return fmt.Sprintf("theError %d %v", t.num, t.err)
}

func TheErrorAsHandler(num int) func(err) TheError {return func(err error) TheError {return theError{ num: i, err: err}
    }
}

另外在日常的 Go 工程中,提案作者认为 CopyFile 函数是新提案语句的一种很好的实际。为此基于 try-handler 进行了一版革新和阐明。

如下代码:

// This helper can be used with defer
func handle(err *error, handler func(err error) error) {
    if err == nil {return nil}
    *err = handler(err)
}

func CopyFile(src, dst string) (err error) {defer handle(&err, func(err error) error {return fmt.Errorf("copy %s %s: %w", src, dst, err)
    })

    r, err := os.Open(src)
    try err
    defer r.Close()

    w, err := os.Create(dst)
    try err, func(err error) error {os.Remove(dst) // only if Create fails
            return fmt.Errorf("dir %s: %w", dst, err)
    }
    defer w.Close()

    err = io.Copy(w, r)
    try err
    err = w.Close()
    try err
    return nil
}

引入 try-hanlder 后,可能做到:

  • 插入谬误的返回语句,进行机制预设。
  • 在返回谬误之前将谬误处理函数组合在一起,便于后续的解决。

总结

在这个新提案中,一旦施行,就能够缩小如下代码的编写:

if err != nil {return ...}

在代码编写上会节俭一些行数,且能够为错误处理机制引入一些新的”操作“,这是该提案的劣势。

然而从 Go 开发者的角度而言,会引入一些新的副作用,例如:初学者的学习老本、Go 工具链的革新、程序了解的复杂度减少。

另外新的语句,仿佛比拟难与 Go1.13 引入的 error.Is 和 As 有较好的相关联性。如果是做一个第三方用户库引入倒能够,但若是作为规范进入 Go 源代码中,仿佛又有些心心相印(提案作者心愿进入)。

看了那么多提案,Go 错误处理机制的”降级“,仿佛陷入了手心手背都是肉的阶段 …

文章继续更新,能够微信搜【脑子进煎鱼了】浏览,本文 GitHub github.com/eddycjy/blog 已收录,学习 Go 语言能够看 Go 学习地图和路线,欢送 Star 催更。

举荐浏览

  • Go 只会 if err != nil?这是不对的,分享这些优雅的解决姿态给你!
  • Go 错误处理新思路?用左侧函数和表达式
  • 先睹为快,Go2 Error 的挣扎之路

Go 图书系列

  • Go 语言入门系列:初探 Go 我的项目实战
  • Go 语言编程之旅:深刻用 Go 做我的项目
  • Go 语言设计哲学:理解 Go 的为什么和设计思考
  • Go 语言进阶之旅:进一步深刻 Go 源码
退出移动版