Go Functions may have multiple return values, by convention the last result is an error indicator or an error. An error indicator is a boolean that will evaluate to true if there is no errors.

func somethingMightGoWrong()(string, bool) {
	return "", false
}

_, ok := somethingMightGoWrong()

if !ok {
	// an error has occurred
}

An error result might be returned if we need more information on the error.

func somethingElseMightGoWrong()(string, error) {
	return "", fmt.Errorf("error: %s", "error description")
}
_, err := somethingElseMightGoWrong()

if err != nil {
    // an error has occurred
}

We can also check for error and execute accordingly.

if _, err := somethingElseMightGoWrong(); err !=nil {
    // do something
}

The downside with that though is that the function results are only visible within the scope of the if statement, which might be ok in some cases.

We can define specific errors for comparison purposes. That is in case the client might want to take different actions based on different errors.

var SpecificError = errors.New("Specific error")

func returnSpecificError() error{
	return SpecificError
}

if err:= returnSpecificError(); err == SpecificError {
		println(err.Error())
	}

Panic and recover

How about throwing an exception? Go give us that in the form of panic which will halt the execution of a program an exit. It also give us a mechanism to recover from panic, that is a panic in let’s say a function we are consuming will not result in halting the program and give us some means to handle the panic (read exception) which is recover. One can think of panic as a throw statement in other programs and recover as a catch in other programs.

func main() {
	defer func() {
		if p:= recover(); p!= nil{
			fmt.Printf("Recovering from our panic! %v", p)  // Recovering from our panic! It is the end of the world!
		}
	}()
	panic("It is the end of the world!")
}

We notice that the recover() built in function would result in a value that might be nil, that happens if no error has occurred, i.e no panic. Notice that recover is called within a deferred anonymous function, so it executes at the end of the function.

We can still have a more traditional error handling if we might be recovering from different types of errors, where we switch on the panic itself in a similar fashion to handling multiple exceptions.

func main() {
	defer func() {
		switch p := recover(); p {
		case nil: // no panic has occurred
		case "It is the end of the world!":
			fmt.Printf("Recovering from our panic! %v", p)
		default:
			fmt.Printf("Recovering from unknown error! %v", p)
		}
	}()
	panic("It is the end of the world!")
}

Resources

  1. Ultimate Go Programming
  2. The Go Programming Language
  3. Go Documentation
  4. A tour of Go
  5. Go wiki: Switch
  6. The Go Programming Language Specification
  7. Go by Example