🐛 Is() and As() functions - check error type in Go

introduction errors

Since Go 1.13, we have new helpful ways to find out the type of error, even if we use error wrapping. If we want to check if a given error matches another specific error, we need to use Is() function from the errors package. If we are interested in whether the error is of a given type, we should call the As() function.

Is function

In the example below, we can see that the function validateInput returns an error for badInput. This error is ErrBadInput wrapped in an error created by fmt.Errorf(). Using the Is(err, target error) bool function, we can detect the ErrBadInput even if it is wrapped since this function checks if any error in the chain of wrapped errors matches the target. Therefore, this form should be preferable to comparison if err == ErrBadInput.

package main

import (
    "errors"
    "fmt"
)

const badInput = "abc"

var ErrBadInput = errors.New("bad input")

func validateInput(input string) error {
    if input == badInput {
        return fmt.Errorf("validateInput: %w", ErrBadInput)
    }
    return nil
}

func main() {
    input := badInput

    err := validateInput(input)
    if errors.Is(err, ErrBadInput) {
        fmt.Println("bad input error")
    }
}

Output:

bad input error

As function

Similar to Is(), the As(err error, target interface{}) bool checks if any error in the chain of wrapped errors matches the target. The difference is that this function checks whether the error has a specific type, unlike the Is(), which examines if it is a particular error object. Because As considers the whole chain of errors, it should be preferable to the type assertion if e, ok := err.(*BadInputError); ok.

target argument of the As(err error, target interface{}) bool function should be a pointer to the error type, which in this case is *BadInputError

package main

import (
    "errors"
    "fmt"
)

const badInput = "abc"

type BadInputError struct {
    input string
}

func (e *BadInputError) Error() string {
    return fmt.Sprintf("bad input: %s", e.input)
}

func validateInput(input string) error {
    if input == badInput {
        return fmt.Errorf("validateInput: %w", &BadInputError{input: input})
    }
    return nil
}

func main() {
    input := badInput

    err := validateInput(input)
    var badInputErr *BadInputError
    if errors.As(err, &badInputErr) {
        fmt.Printf("bad input error occured: %s\n", badInputErr)
    }
}

Output:

bad input error occured: bad input: abc

🕵️ Solve 'cannot take address of XXX' error in Go

Learn how to take the address of a literal, map value, or function return value
introduction pointer errors

📂 Check if a file exists in Go

Learn how to check if a file exists in Go after or before opening it
introduction file errors

🖨️ Convert string to []byte or []byte to string in Go

Learn the difference between a string and a byte slice
introduction strings slice