2013-03-13 104 views
8

有没有办法清理这个(IMO)可怕的代码?Go - 优雅地处理多个错误?

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 
} 

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

+0

标题是关于错误处理,但你似乎正在返回它们而不是[恐慌()](http://golang.org/doc /effective_go.html#panic),这是为什么?马歇尔一失败就马上不立即回国? – Deleplace 2013-03-13 23:39:00

+6

为什么我会使用panic()而不是返回错误?从链接到的文档中:“向调用者报告错误的常用方法是将错误作为额外的返回值返回。”我觉得这是一个“常见”的情况。使用panic()感觉是合理的,这不是严重的错误。 (如果我误解了恐慌函数的用例,请纠正我。) – 2013-03-13 23:54:02

+3

我知道你已经注意到了每个gopher会同意什么,不使用/滥用恐慌,但上面的代码示例非常糟糕/模糊。把所有东西都编成数字,但只返回我们之后看到的第一个错误?但是,接下来你会澄清“一次处理所有错误”,通常会使代码示例无效。这又取决于你的行为,恐慌/恢复可能是合适的。就我个人而言,我会立即在一位失败的元帅身上发现错误,并更具描述性地指出“cJson”失败,并附有“err”。但即使如此,仍取决于所运行的环境。 – dskinner 2013-03-14 14:02:12

回答

14
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 
+0

编辑需要最少6个字符,所以不能触摸它,但在声明'f'时需要删除'()'。 – dskinner 2013-03-13 23:14:22

+0

@dskinner:修正,谢谢。 – zzzz 2013-03-14 07:03:26

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

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

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 
} 

你没有说你希望你的错误处理如何工作。失败一个,全部失败?首先失败?收集成功或折腾他们?

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和专门处理每个错误时。另一个不利的方面是它可以防止恐慌实际上发生恐慌。 (但这可以通过使用你自己的错误类型来解决。)