Chapter 12 - Recovering from Failure

Exercise 1: defer

We need to be sure our Camp function calls the Fire value’s Extinguish method before the program exits. But right now, Camp is returning early due to an error, before Extinguish can be called.

We don’t want to leave a lit Fire unattended in the forest! Revise the Camp function so that Extinguish is always called, even if Camp returns early.

package main

import "fmt"

type Fire struct {
	lit bool
}

func (f Fire) Light() {
	f.lit = true
	fmt.Println("Fire lit:", f.lit)
}
func (f Fire) Extinguish() {
	f.lit = false
	fmt.Println("Fire lit:", f.lit)
}

func Camp() error {
	var fire Fire
	fire.Light()
	return fmt.Errorf("spotted a bear")
	fmt.Println("Toasting marshmallows")
	fire.Extinguish()
	return nil
}

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

Actual Output:

Fire lit: true
Error: spotted a bear

Desired Output:

Fire lit: true
Fire lit: false
Error: spotted a bear

When you’re ready, have a look at our solution.

Exercise 2: panic and recover

Go through the following code samples, and predict what their output will be. No need to predict all the details of the stack traces; focus on determining which fmt.Println calls will get made, and in what order.

Solution

Example 1

package main

import "fmt"

func main() {
	fmt.Println("a")
	panic("oh no")
	fmt.Println("b")
}

Example 2

package main

import "fmt"

func myFunction() {
	defer fmt.Println("b")
	panic("oh no")
}

func main() {
	fmt.Println("a")
	myFunction()
}

Example 3

package main

import "fmt"

func myFunction() {
	panic("oh no")
	fmt.Println(recover())
}

func main() {
	fmt.Println("a")
	myFunction()
	fmt.Println("b")
}

Example 4

package main

import "fmt"

func otherFunction() {
	fmt.Println("c")
	fmt.Println(recover())
}

func myFunction() {
	defer otherFunction()
	panic("oh no")
	fmt.Println("d")
}

func main() {
	fmt.Println("a")
	myFunction()
	fmt.Println("b")
}

Here are the solutions.