Go - 优雅地处理多个错误?

时间:2013-03-13 22:06:41

标签: 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 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
} 

具体来说,我在谈论错误处理。能够一次性处理所有错误会很好。

5 个答案:

答案 0 :(得分:15)

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

答案 1 :(得分:13)

将结果放在切片而不是变量中,将初始值放在另一个切片中进行迭代,如果出现错误则在迭代期间返回。

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

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

答案 2 :(得分:7)

定义一个函数。

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
}

您没有说明您希望错误处理如何工作。失败一个,失败了吗?首先失败?收集成功或抛弃它们?

答案 3 :(得分:5)

我相信这里的其他答案对于您的具体问题是正确的,但更一般地,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并专门处理每个错误。另一个缺点是,它可能会阻止应该恐慌的东西实际上被恐慌。 (但这可以通过使用您自己的错误类型来解决。)

答案 4 :(得分:0)

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

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
}