【问题标题】:Writing generic error handling function without generics编写没有泛型的泛型错误处理函数
【发布时间】:2022-01-28 09:19:08
【问题描述】:

我知道 Go 将来不会有泛型,并且有一些建议可以用其他构造替换它们。但是在下面的示例中,我陷入了困境。

func P(any interface{}, err error) (interface{}) {
    if err != nil {
        panic("error: "+ err.Error())
    }
    return any
}

正如您可能猜到的那样,我试图在任何错误上都失败,并希望将P() 放在返回两个结果且第二个是错误的任何函数周围。这工作正常,但any 丢失了它的类型信息,结果中只是一个空接口。

由于我也在调用 lib 函数,因此我看不到使用接口或反射解决此问题的方法。

有什么想法吗?我是完全走错了路还是离目标很近?

【问题讨论】:

  • 别这样。只需写if err != nil 很多时间,它就不会那么烦人了。如果你写了一些完全重复的err != nils 构成了你的代码太多的东西,你可以write helpers that assign the error somewhere,或者如果某些或所有错误肯定会影响整个操作,请编写一个panic-on-unrecoverable-error wrapper,如mustWrite然后recover 在它从你的包裹里出来之前从恐慌中解脱出来。
  • 我建议你停止尝试将“语言 X 的做事方式”融入 Go 代码。 Go 到处都有明确的错误处理,没有真正的异常(恐慌/恢复与 C++/Java 异常不一样)。特别是,不要永远编写一个恐慌的 Go 包,例如 os.Open 返回一个 os.PathError
  • 即使你曾经找到一个你想对错误感到恐慌的地方,只要panic(err)。如果未恢复,输出将几乎相同,但recover 将得到原始错误而不是字符串表示形式。

标签: generics error-handling go


【解决方案1】:

一种解决方案是 go generate 您的 P() 函数,为您需要使用的每种具体类型提供一个。
请参阅以下示例:

这将使调用这些 lib 函数更容易,因为生成的具体 P () 实现将使用正确的类型而不是 interface{}。

【讨论】:

    【解决方案2】:

    你想做的事情需要泛型,但正如你已经提到的,Go 不支持泛型类型。因此,您不能创建一个不会丢失类型的通用函数。

    您必须为要支持的每种类型创建这样的函数。请注意,标准库已经包含其中一些名称为 MustXXX(),您可以直接使用它们,例如:

    template.Must(t *Template, err error) *Template

    或抑制error的“类似”功能,但如果仍然发生,则会出现恐慌,例如:

    regexp.MustCompile(str string) *Regexp(抑制error,但如果str 不是有效的正则表达式,则会出现恐慌)

    【讨论】:

      【解决方案3】:

      如果您打算只对错误感到恐慌(坏主意) 或记录它们,那么只需定义一个函数来执行此操作并使用它。例如

      func checkErr(err error) {
          if err != nil {
              log.Println(err)
          }
      }
      
      // ...
      
      func foo() {
          a, err := doA()
          checkErr(err)
          b, err := doB()
          checkErr(err)
          // etc.
      }
      

      用户 twotwotwo 已经链接到 Errors are values 文章,该文章展示了更多关于如何减少错误处理重复性的示例。但我建议只写整个if err != nil 的东西,因为根据我的经验,每三个错误,如果不是第二个,都需要一些额外的处理。

      【讨论】:

        【解决方案4】:

        Go 1.18(2022 年初)将向该语言引入类型参数。

        基于当前的 accepted proposal 规范,您将能够编写通用的 Must 函数而不牺牲类型安全。

        它将如下所示:

        package main
        
        import (
            "fmt"
            "errors"
        )
        
        func Must[T any](v T, err error) T {
            if err != nil {
                panic("error: " + err.Error())
            }
            return v
        }
        
        func main() {
            fmt.Println(Must(test1())) // 450
            fmt.Println(Must(test2())) // panics...
        }
        
        func test1() (int, error) {
            return 450, nil
        }
        
        func test2() (string, error) {
            return "", errors.New("problem")
        }
        

        Go2 操场:https://go2goplay.golang.org/p/SHqZc5LYeAB

        【讨论】:

          猜你喜欢
          • 2018-01-01
          • 2010-09-15
          • 1970-01-01
          • 1970-01-01
          • 2020-10-26
          • 1970-01-01
          • 2015-08-11
          • 1970-01-01
          相关资源
          最近更新 更多