Errors

An Introduction to Errors

Errors are treated as values in Go. The error type is a built-in interface. An error variable represents any value that can describe itself as a string.

 type error interface {
    Error() string
 }

Functions often return an error value, and calling code should handle errors by testing whether the error equals nil.

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err)
    return
}
fmt.Println("Converted integer:", i)

The most commonly-used error implementation is the errors package’s unexported errorString type.

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

You can construct one of these values with the errors.New function. It takes a string that it converts to an errors.errorString and returns as an error value.

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

Here’s how you might use errors.New:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // implementation
}

A caller passing a negative argument to Sqrt receives a non-nil error value (whose concrete representation is an errors.errorString value). The caller can access the error string (“math: square root of…”) by calling the error’s Error method, or by just printing it:

f, err := Sqrt(-1)
if err != nil {
    fmt.Println(err)
}

It’s possible to use custom types as errors by implementing the Error() method on them.

package main

import (
	"fmt"
)

type MyError struct {
	Msg string
	When time.Time
}

func (e *MyError) Error() string {
	return fmt.Sprintf("Oops!  %s at %v", e.Msg,e.When)
}

func run() error {
	return &MyError{
	Msg:	"It didn't work",
	}
}

func main() {
	if err := run(); err != nil {
		fmt.Println(err)
	}
}

Output:

Oops!  It didn't work at 0001-01-01 00:00:00 +0000 UTC
  • Question: How does the above code works? We are passing the error interface to fmt.Println(err), But how does it print the string “Oops! It didn’t work at 0001-01-01 00:00:00 +0000 UTC” from fmt.Println(err)?

    The fmt package formats an error value by calling its Error() string method.

To find out the answer, you have to go through the source code of package “fmt”. Here is the code snippet from the package “fmt” that actually implements this functionality

switch v := p.arg.(type) {
			case error:
				handled = true
				defer p.catchPanic(p.arg, verb, "Error")
				p.fmtString(v.Error(), verb)
				return

			case Stringer:
				handled = true
				defer p.catchPanic(p.arg, verb, "String")
				p.fmtString(v.String(), verb)
				return
			}

In our code, argument of Println() = err which is of type *MyError that implements the error interface. Hence it calls the method Error() of the *MyError object.

So, in this case

fmt.Println(err)

is equivalent to

fmt.Println(err.Error())

Example:

package main

import (
	"fmt"
	"time"
)

type MyError struct {
	Msg string
	When time.Time
}

func (e *MyError) Error() string {
	return fmt.Sprintf("Oops!  %s at %v", e.Msg,e.When)
}

func run() error {
	return &MyError{
	Msg:	"It didn't work",
	}
}

func main() {
     e := &MyError{
      Msg:"It didn't work",
     }
	if err := run(); err != nil {
		fmt.Println(err.Error())
		fmt.Println(err)
		fmt.Println(e.Error())
	}
}

Output:

Oops!  It didn't work at 0001-01-01 00:00:00 +0000 UTC
Oops!  It didn't work at 0001-01-01 00:00:00 +0000 UTC
Oops!  It didn't work at 0001-01-01 00:00:00 +0000 UTC

Tiya Jose ©2019. All rights reserved.

Powered by Hydejack v8.5.1