Go—;优雅地处理多个错误?

Go—;优雅地处理多个错误?,go,Go,有没有办法清理这个(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 err

有没有办法清理这个(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
} 

具体来说,我说的是错误处理。如果能够一次性处理所有错误就好了。

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

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
    }
}
当然,这需要一个类型断言来使用结果。

定义一个函数

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
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
}

你没有说你希望你的错误处理如何工作。一败涂地,一败涂地?第一个失败?收集成功或扔掉它们?

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

考虑:

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
}
每当封送值出现问题时,此代码使用
mustmarshall
panic
。但是
encodeAll
功能将
从死机中恢复
,并将其作为正常错误值返回。在这种情况下,客户永远不会面临恐慌

但这也带来了一个警告:在任何地方使用这种方法都不是惯用的。它也可能更糟,因为它不能很好地处理每个单独的错误,但或多或少地将每个错误处理为相同的。但当有大量错误需要处理时,它也有它的用处。例如,我在web应用程序中使用这种方法,其中顶级处理程序可以捕获不同类型的错误,并根据错误的类型将它们适当地显示给用户(或日志文件)


当有大量的错误处理时,它会使代码更加简洁,但却失去了惯用的Go和专门处理每个错误的能力。另一个不利的方面是,它可以防止应该恐慌的事情实际上恐慌起来。(但这可以通过使用您自己的错误类型轻松解决。)

您可以创建一个可重用的方法,然后只捕获一个if条件下的错误。不过,此实现将只显示最后一个错误

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

aJson, err := 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, err7 := json.Marshal(g)

if error := util.hasError(err, err1, err2, err3, err4, err5, err6, err7); error != nil {
    return error
}

“编辑”至少需要6个字符,因此无法触摸它,但在声明
f
时需要删除
()
。标题是关于错误处理的,但您似乎返回了它们而不是去,这是为什么?还有1 Marshall失败后没有立即返回?为什么我要使用panic()而不是返回错误?从您链接到的文档中可以看到:“向调用者报告错误的通常方法是将错误作为额外的返回值返回。”我觉得这是一种“常见”的情况。使用panic()并不是一个如此严重的错误。(如果我误解了惊慌功能的用例,请纠正我。)我知道你在声明每一个地鼠都会同意的内容上得到了支持,没有使用/滥用惊慌,但上面的代码示例非常糟糕/模糊。封送所有内容,但只返回我们随后看到的第一个错误?但随后您澄清了“一次性处理所有错误”,主要是使代码示例无效。同样,这取决于你的行为,恐慌/恢复可能是合适的。就我个人而言,我会在一个失败的封送处理上立即出错,并更详细地描述
cJson
失败,并附加
err
。但即便如此,这也取决于它运行的环境。好的观点。我给出的代码模棱两可。我会在每次marshall通话后检查是否有错误——唯一让我无法接受的是,我认为将marshall通话分组看起来更整洁。依我看,不是所有的错误都会被返回并不重要。我所追求的行为是“返回第一个非零错误”。这个答案应该是最好的答案标签!