Unwrap

(feature added in Go 1.13)

This is an interesting feature, when building errors that contain other errors.

Unwrap is actually defined as follows:

func Unwrap(err error) error {

	u, ok := err.(interface {
		Unwrap() error
	})
	if !ok {
		return nil
	}
	return u.Unwrap()

}

It first checks whether err has itself the Unwrap method. If it doesn't, then nothing else can be done. There is nothing to unwrap. Nada. But if the method is defined, it calls it and returns its value, which is an error itself.

This is a very useful way of composing errors, and thus adding much more information. We could think of the following scenario:

package main

import (
	"fmt"
	"log"
	"errors"
)
type databaseError struct {
    err error
}

type queryMissingParamsError struct {
    params string
}

func (de *databaseError) Unwrap() error {
    return de.err
}

func (de *databaseError) Error() string {
    return fmt.Sprintf("Database error. %s", de.err)
}

func (qmp *queryMissingParamsError) Error() string {
    return fmt.Sprintf("Some parameter were missing: %s", qmp.params)
}

// bogus query
type Query struct {}

func (q *Query) Check() error {
    return &databaseError {
        err: &queryMissingParamsError {
            params: "name",
        },
    }
}

func handleDatabaseQuery(query *Query) error {
    err := query.Check()
    if err == nil {
        return nil
    }
    log.Print(err)
    return errors.Unwrap(err)
}
func main() {
	q := &Query{}
	err := handleDatabaseQuery(q)
	fmt.Println(err)
}

In this example, we can see how an error can pack and unpack other errors. And we can choose to define a strategy for the different scenarios. So, in this particular example, we could ask the user to make the query again and remind her not to forget to include name as a paramter.