编写不使用泛型的通用错误处理函数。

huangapple go评论68阅读模式
英文:

Writing generic error handling function without generics

问题

我知道Go语言将来不会有泛型,并且有一些建议用其他结构替代泛型。但是在下面的例子中,我遇到了困难。

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

你可能猜到了,我试图在任何返回两个结果且第二个结果是错误的函数周围使用P()。这个方法运行良好,但是any失去了类型信息,只是一个空接口。

由于我还调用了库函数,我看不到使用接口或反射来解决这个问题的方法。

有什么想法吗?我是完全错了还是接近目标了?

英文:

I know that Go will not have generics in the future and there are some recommendations to replace them by other constructs. But with my example below I got stuck.

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

As you might guess, I'm trying to just fail on any error and want to put P() just around any function that is returning two results and the second is an error. This is working fine, but any is losing it's type information and is only an empty interface in the result.

As I'm also calling lib functions I don't see a way to address this with Interfaces or Reflection.

Any ideas? Am I totally on the wrong track or close to the goal?

答案1

得分: 12

一种解决方案是使用go generate来生成你的P()函数,为每个需要处理的具体类型生成一个函数。可以参考以下示例:

这样做可以使调用这些库函数更加方便,因为生成的具体的P()实现将使用正确的类型,而不是interface{}。

英文:

One solution would be to go generate your P() function, one for each concrete type you need to work with.
See examples in:

That would make calling those lib functions easier, since the concrete P () implementations generated would use the right type instead of interface{}.

答案2

得分: 7

Go 1.18 泛型更新:Go 1.18 增加了对泛型的支持,现在可以编写一个通用的 Must() 函数:

func Must[T any](v T, err error) T {
    if err != nil {
        panic(err)
    }
    return v
}

这个函数可以在 github.com/icza/gog 中找到,作为 gog.Must() 使用(声明:我是作者)。

原始的、1.18 之前的回答如下。


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

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

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

或者类似的函数,它们会忽略 error,但如果仍然发生错误,会引发 panic,例如:

regexp.MustCompile(str string) *Regexp(忽略 error,但如果 str 不是有效的正则表达式,则会引发 panic)

英文:

Go 1.18 generics update: Go 1.18 adds generics support, it is now possible to write a generic Must() function:

func Must[T any](v T, err error) T {
	if err != nil {
		panic(err)
	}
	return v
}

This is available in github.com/icza/gog, as gog.Must() (disclosure: I'm the author).

Original, pre-1.18 answer follows.


What you want to do would require generics but as you already mentioned, Go does not support generic types. Therefore, you can't create a general function which would not lose the type.

You have to create such a function for each type you want to support. Note that the standard library already contains some of these going under the name MustXXX(), which you can use out of the box, for example:

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

Or "similar" functions which suppress the error but if one still occurs, panics, for example:

regexp.MustCompile(str string) *Regexp (suppresses error but panics if str is not a valid regexp)

答案3

得分: 4

如果你计划只是在出现错误时恐慌(不好的主意)或记录错误,那么只需定义一个函数来处理并使用它。例如:

func checkErr(err error) {
    if err != nil {
        log.Println(err)
    }
}

// ...

func foo() {
    a, err := doA()
    checkErr(err)
    b, err := doB()
    checkErr(err)
    // 等等。
}

用户twotwotwo已经链接到了《Errors are values》(http://blog.golang.org/errors-are-values)一文,其中展示了更多关于如何使错误处理更少重复的示例。但是根据我的经验,我建议直接写整个if err != nil的判断,因为在我经验中,每三个错误中,如果不是每两个错误,都需要一些额外的处理。

英文:

If you plan on just panicking on errors (bad idea) or logging them, then just define a function to do so and use it. E.g.

func checkErr(err error) {
    if err != nil {
        log.Println(err)
    }
}

// ...

func foo() {
    a, err := doA()
    checkErr(err)
    b, err := doB()
    checkErr(err)
    // etc.
}

The user twotwotwo has already linked to the Errors are values article that shows more examples on how to make error handling less repetitive. But I would recommend just write the whole if err != nil thing, because in my experience every third error, if not second, requires some additional handling.

答案4

得分: 1

在Go 1.18(2022年初)中,将引入类型参数到该语言中。

根据当前已接受的 提案规范,您将能够编写一个通用的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 playground: https://go2goplay.golang.org/p/SHqZc5LYeAB

英文:

With Go 1.18 (early 2022) type parameters will be introduced to the language.

Based on the current accepted proposal specs, you will be able to write a generic Must function without sacrificing type safety.

It will look like the following:

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 playground: https://go2goplay.golang.org/p/SHqZc5LYeAB

huangapple
  • 本文由 发表于 2015年3月18日 16:26:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/29117141.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定