Using Generics for creating custom error wrapping types...?

220 views
Skip to first unread message

TheDiveO

unread,
Jun 19, 2022, 5:03:06 PM6/19/22
to golang-nuts
My limited understanding of Go error "kinds" is as follows:
  1. error values of arbitrary type, where the type doesn't matter: such as returned by errors.New("D'OH!").
  2. sentinel(?) errors, such as io.EOF: when comparing against the same error instance.
  3. wrapped errors, where the type of the wrapper (type) doesn't matter: such as created by fmt.Errorf("...%w", err)
  4. ...
Now, in some situations I would like to use typed errors which additionally wrap underlying errors. Sentinel (2) errors don't work, as due to the wrapping there would be an open number of different values with no chance of classification. And (3) doesn't allow individual typing either.

In order to create individual error types that additionally wrap underlying one can either write the same following boilerplate over and over again, or alternatively use embedding in addition with method propagation. For instance:

type WrappingError struct {
   msg string
   err error
}

func (w *WrappingError) Error() string {
   return w.msg
}

func (w *WrappingError) Unwrap() error {
   return w.err
}

type AErr struct{ WrappingError }
type BErr struct{ WrappingError }

...this gives individual error types AErr and BErr without having to copy-and-paste everything everytime a new wrapping error type is needed.

Now with generics I understand that I can't do anything about the type AErr ... boilerplate. However, what about at least making creating new typed wrapping errors less of a boilerplate pain?

Naively, I tried...

func New[T ~struct{ WrappingError }](msg string, err error) error {
   e := T{WrappingError{msg: msg, err: err}}
   return &e
}

...but this get's rejected in Go 1.19: cannot use &e (value of type *T) as type error in return statement:    *T does not implement error (type *T is pointer to type parameter, not type parameter)

I would have liked to use it like this:

err := New[AErr]("this is an error", errors.New("foo"))

Any idea how this could be coded differently to somehow achieve the goal of reducing boilerplate pain when it comes to typed wrapping errors? Or is this a perfect example of attempting to throw Go generics at something generics are not supposed to support...?

Example on the "Better Go Playground": https://goplay.tools/snippet/NNPL5m5VekG

TheDiveO

unread,
Jun 19, 2022, 5:05:59 PM6/19/22
to golang-nuts
erm, Go 1.18 and not Go 1.19, sorry.

Ian Lance Taylor

unread,
Jun 20, 2022, 1:49:29 AM6/20/22
to TheDiveO, golang-nuts
Related discussion at https://go.dev/issue/53435 which links to
several other related issues.

Ian

TheDiveO

unread,
Jun 20, 2022, 2:11:34 AM6/20/22
to golang-nuts
Hi Ian,

if I understand the linked proposal correct then it is about wrapping multiple errors. I'm unsure how this relates to my question about having separate error types as to differentiate them, and each type always only wraps a single error. How do I connect the dots?

Ian Lance Taylor

unread,
Jun 20, 2022, 2:26:16 AM6/20/22
to TheDiveO, golang-nuts
On Sun, Jun 19, 2022 at 11:11 PM TheDiveO <harald....@gmx.net> wrote:
>
> if I understand the linked proposal correct then it is about wrapping multiple errors. I'm unsure how this relates to my question about having separate error types as to differentiate them, and each type always only wraps a single error. How do I connect the dots?

It seemed like a similar concept to me. Likely I misunderstood. Apologies.

One way to write code similar to what you wrote above is

type WrappingError struct {
msg string
err error
}

func (w *WrappingError) Error() string {
return w.msg
}

func (w *WrappingError) Unwrap() error {
return w.err
}

type AErr struct{ *WrappingError }
type BErr struct{ *WrappingError }

func New[T ~struct{ *WrappingError }](msg string, err error) error {
e := T{&WrappingError{msg: msg, err: err}}
return error(e)
}

Ian
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/2c284149-7053-4f94-8903-3cfd7288a935n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages