Go语言 优雅地处理多个错误?

lh80um4z  于 2022-12-25  发布在  Go
关注(0)|答案(6)|浏览(134)

有没有办法清理这个(IMO)看起来很可怕的代码?

aJson, err1 := json.Marshal(a)
bJson, err2 := json.Marshal(b)
cJson, err3 := json.Marshal(c)
dJson, err4 := json.Marshal(d)
eJson, err5 := json.Marshal(e)
fJson, err6 := json.Marshal(f)
gJson, err4 := json.Marshal(g)
if err1 != nil {
    return err1
} else if err2 != nil {
    return err2
} else if err3 != nil {
    return err3
} else if err4 != nil {
    return err4
} else if err5 != nil {
    return err5
} else if err5 != nil {
    return err5
} else if err6 != nil {
    return err6
}

具体地说,我是在谈论错误处理。如果能够一次性处理所有的错误,那就太好了。

pod7payv

pod7payv1#

var err error
f := func(dest *D, src S) bool {
    *dest, err = json.Marshal(src)
    return err == nil
} // EDIT: removed ()

f(&aJson, a) &&
    f(&bJson, b) &&
    f(&cJson, c) &&
    f(&dJson, d) &&
    f(&eJson, e) &&
    f(&fJson, f) &&
    f(&gJson, g)
return err
yws3nbqq

yws3nbqq2#

把结果放在一个切片中而不是变量中,把初始值放在另一个切片中进行迭代,如果有错误,在迭代过程中返回。

var result [][]byte
for _, item := range []interface{}{a, b, c, d, e, f, g} {
    res, err := json.Marshal(item)
    if err != nil {
        return err
    }
    result = append(result, res)
}

您甚至可以重用一个数组,而不是使用两个切片。

var values, err = [...]interface{}{a, b, c, d, e, f, g}, error(nil)
for i, item := range values {
    if values[i], err = json.Marshal(item); err != nil {
        return err
    }
}

当然,这需要一个类型Assert来使用结果。

yeotifhr

yeotifhr3#

定义一个函数。

func marshalMany(vals ...interface{}) ([][]byte, error) {
    out := make([][]byte, 0, len(vals))
    for i := range vals {
        b, err := json.Marshal(vals[i])
        if err != nil {
            return nil, err
        }
        out = append(out, b)
    }
    return out, nil
}

你没有提到你希望你的错误处理方式。2失败一个,失败所有?3第一个失败?4收集成功还是丢弃它们?

wkyowqbh

wkyowqbh4#

我相信这里的其他答案对于您的特定问题是正确的,但更一般地说,panic可以用来缩短错误处理,同时仍然是一个性能良好的库(即,panic不能跨越包边界)。
考虑:

func mustMarshal(v interface{}) []byte {
    bs, err := json.Marshal(v)
    if err != nil {
        panic(err)
    }
    return bs
}

func encodeAll() (err error) {
    defer func() {
        if r := recover(); r != nil {
            var ok bool
            if err, ok = r.(error); ok {
                return
            }
            panic(r)
        }
    }()

    ea := mustMarshal(a)    
    eb := mustMarshal(b)
    ec := mustMarshal(c)

    return nil
}

每当封送值出现问题时,此代码使用mustMarshalpanic。但是encodeAll函数将recover从死机中删除,并将其作为正常错误值返回。在这种情况下,客户端永远不会出现死机。
但这也伴随着一个警告:在任何地方使用这种方法都是不习惯的。它还可能更糟,因为它不能很好地处理每一个单独的错误,而是或多或少地对待每个错误。但当有大量的错误需要处理时,它还是有它的用处的。作为一个例子,我在一个Web应用程序中使用了这种方法,其中顶层处理程序可以捕获不同种类的错误,并根据错误的种类将它们适当地显示给用户(或日志文件)。
当有很多错误处理时,它会使代码更简洁,但会失去Go语言的惯用性,也会对每个错误进行特殊处理。另一个缺点是,它可能会阻止一些"应该"恐慌的东西真正恐慌(但这可以通过使用自己的错误类型来轻松解决)。

monwx1rj

monwx1rj5#

您可以使用Hashicorp的go-multierror

var merr error

if err := step1(); err != nil {
    merr = multierror.Append(merr, err)
}
if err := step2(); err != nil {
    merr = multierror.Append(merr, err)
}

return merr
lyr7nygr

lyr7nygr6#

您可以创建一个可重用的方法来处理多个错误,此实现将只显示最后一个错误,但您可以通过修改以下代码来返回每个错误消息组合:

func hasError(errs ...error) error {
    for i, _ := range errs {
        if errs[i] != nil {
            return errs[i]
        }
    }
    return nil
}

aJson, err := json.Marshal(a)
bJson, err1 := json.Marshal(b)
cJson, err2 := json.Marshal(c)

if error := hasError(err, err1, err2); error != nil {
    return error
}

相关问题