【问题标题】:Go — handling multiple errors elegantly?Go——优雅地处理多个错误?
【发布时间】:2013-03-13 22:06:40
【问题描述】:

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

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

【问题讨论】:

  • 标题是关于错误处理的,但你似乎是在返回它们而不是去panic(),有什么原因吗?也因为 1 Marshall 失败后没有立即返回?
  • 为什么我要使用 panic() 而不是返回错误?从您链接到的文档中:“向调用者报告错误的通常方法是将错误作为额外的返回值返回。”我觉得这是一个“通常”的情况。使用 panic() 感觉合理,这并不是一个如此严重的错误。 (如果我误解了恐慌功能的用例,请纠正我。)
  • 我知道你因为陈述每个 gopher 都会同意的内容而不是使用/滥用恐慌而获得了投票,但上面的代码示例真的很糟糕/含糊不清。元帅一切,但只返回我们之后看到的第一个错误?但是随后您澄清了“一次性处理所有错误”,主要是使代码示例无效。同样,这取决于您在做什么,并且恐慌/恢复可能是合适的。就个人而言,我会在失败的元帅上立即出错,并且更描述cJson 失败,并附上err。但即便如此,这也取决于运行的环境。
  • 好点。我给出的代码模棱两可。我会在每个 marshall 之后检查一个错误——唯一阻止我的是我认为将 marshall 调用组合在一起看起来更整洁。 IMO,并非所有错误都会返回并不重要。我所追求的行为是“返回第一个非零错误。”

标签: go


【解决方案1】:
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

【讨论】:

  • 编辑需要最少 6 个字符,因此不能触摸它,但在声明 f 时需要删除 ()
【解决方案2】:

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

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

当然,这需要类型断言才能使用结果。

【讨论】:

  • 这个答案值得最佳答案标签!
【解决方案3】:

定义一个函数。

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
}

您没有说明您希望错误处理如何工作。失败一个,失败所有?第一个失败?收集成功还是扔掉它们?

【讨论】:

    【解决方案4】:

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

    考虑:

    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 并专门处理每个错误。另一个缺点是它可以防止某些应该恐慌的东西真正恐慌。 (但这可以通过使用您自己的错误类型轻松解决。)

    【讨论】:

      【解决方案5】:

      您可以创建一个可重用的方法,然后仅在一个 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】:

        您可以使用 Hashicorp 的go-multierror

        var result error
        
        if err := step1(); err != nil {
            result = multierror.Append(result, err)
        }
        if err := step2(); err != nil {
            result = multierror.Append(result, err)
        }
        
        return result
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-05-15
          • 1970-01-01
          • 1970-01-01
          • 2020-03-19
          • 1970-01-01
          • 2011-08-08
          • 1970-01-01
          • 2021-05-26
          相关资源
          最近更新 更多