Reputation: 1065
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?
Upvotes: 16
Views: 4147
Reputation: 44665
You can write a generic Must
function without sacrificing type safety. It looks 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")
}
Playground: https://go.dev/play/p/U1MIHDic9Bn
Upvotes: 1
Reputation: 417592
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)
Upvotes: 9
Reputation: 1324093
One solution would be to go generate
your P()
function, one for each concrete type you need to work with.
See examples in:
go generate
"".That would make calling those lib functions easier, since the concrete P () implementations generated would use the right type instead of interface{}.
Upvotes: 13
Reputation: 36199
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.
Upvotes: 4