Home >Backend Development >Golang >Go try Is the new proposal reliable? Want to simplify error handling

Go try Is the new proposal reliable? Want to simplify error handling

Golang菜鸟
Golang菜鸟forward
2023-08-04 17:05:371282browse


In the recent new try proposal "proposal: Go 2: error handling: try statement with handler[1]》has sparked heated discussions in the community, let’s fight the community again! ! !

Today Jianyu will open it with everyone to see if this can open up and reorganize the Go error handling mechanism.

Background

The proposal author @Greg Weber from PingCAP will do this based on two factors, one is in the "Go Developer Survey 2022 Q2 Results[2]" is clearly mentioned.

Go try Is the new proposal reliable? Want to simplify error handling

With the release of Go1.18 generics, the original most contradictory generics have received a preliminary solution. According to community surveys, the biggest challenge faced by developers when using Go has shifted to error handling, and energy needs to be invested to "solve" it.

Another factor is that it is well known that Go error handling code is relatively cumbersome. Engineers often joke that 30% of a Go project has if err = nil.

The following code:

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

I hope to make it more elegant. Many friends also agree with this design. It is indeed a simple and intuitive processing, which has formed a struggle in the community.

try-handler proposal

The solution mentioned in this proposal is to add a new statement try to achieve concise error handling. Make the processing of if err != nil smooth.

The following code:

try err, handler

The code generated after translation by the compiler:

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

In the function, it can be as follows:

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

The code generated after translation:

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

It can also be processed only if err != nil. The following code:

try err

The translated code:

if err != nil {
    return err
}

will not call the non-existent handler for processing, and will return directly. Three lines (if err != nil logic) directly change 3 words (try).

If you don’t want to write a function, you can also directly:

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

The scenario for defer try can be as follows:

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

The input parameters are more flexible, and the author hopes that it will be generic. This can adapt to the requirements of various scenarios.

示例和实践

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

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 错误处理机制的 ”升级“,似乎陷入了手心手背都是肉的阶段...

The above is the detailed content of Go try Is the new proposal reliable? Want to simplify error handling. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:Golang菜鸟. If there is any infringement, please contact admin@php.cn delete