Go - Control flows - Defer, panic and recover functions

Source: Use control flows in Go

Defer function

The defer statement postpones the running of a function (including any parameters) until the function that contains the defer statement finishes.

When defer a function: when you want to avoid forgetting about tasks like closing a file or running a cleanup process.

The defer statements run in reverse order, from last to first.

E.g.

package main

import "fmt"

func main() {
    for i := 1; i <= 4; i++ {
        defer fmt.Println("deferred", -i)
        fmt.Println("regular", i)
    }
}

Every time fmt.Println("deferred", -i) was deferred, the value for i was stored, and its run task was added to a queue. When the function returns, the queue is processed, and the tasks are executed in reverse order.

Output:

regular 1
regular 2
regular 3
regular 4
deferred -4
deferred -3
deferred -2
deferred -1

E.g: close a file after write on it

After you create or open a file, you defer the .Close() function to avoid forgetting to close the file after you’re done.

func main() {
    newfile, error := os.Create("learnGo.txt")
    if error != nil {
        fmt.Println("Error: Could not create file.")
        return
    }
    defer newfile.Close()

    if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
	    fmt.Println("Error: Could not write to file.")
        return
    }

    newfile.Sync()
}

Panic function

The panic() function stops the normal flow of control in a Go program. The program then crashes with a log message (you can add any value as an argument but usually you send an error message about why you’re panicking).

You can combine it with the defer statement, as the defer will be executed after the panic.

E.g:

func highlow(high int, low int) {
    if high < low {
        fmt.Println("Panic!")
        panic("highlow() low greater than high")
    }
    defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
    fmt.Println("Call: highlow(", high, ",", low, ")")

    highlow(high, low + 1)
}

func main() {
    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

When the value of low is greater than the value of high, the program panics. You see the Panic! message. At this point, the control flow is interrupted, and all the deferred functions start to print the Deferred... message.

Then the program crashes.

Output:

Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
panic: highlow() low greater than high

...

Recover function

Like the panic but instead of crash, you regain control after a panic.

The previous example but using the revocer() instead to recover from the panic:

func main() {
    defer func() {
	  handler := recover()
        if handler != nil {
            fmt.Println("main(): recover", handler)
        }
    }()

    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}
Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
main(): recover from panic highlow() low greater than high

Program exited.

The combination of the panic and recover functions is the idiomatic way that Go handles exceptions.