5. Defer
● A defer statement allows you to execute a function when the calling function returns
● When you call the defer statement, the given function is pushed into a list
● After the surrounding function returned, functions in the list are executed in LIFO order
● Defer is commonly used to improve readability
9. 1. A deferred function's arguments are evaluated when the defer
statement is evaluated.
func main() {
i := 0
defer fmt.Println(i)
i++
}
func main() {
i := 0
defer func() {
fmt.Println(i)
}()
i++
}
0 1
10. func main() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
2. Deferred function calls are executed in Last In First Out order
after the surrounding function returns.
3210
11. func f() (i int) {
defer func() { i++ }()
return 2
}
func main() {
fmt.Println(f())
}
3. Deferred functions may read and assign to the returning
function's named return values.
3
13. Panic
● A built-in function from the runtime
● When the function F calls the panic function
○ It stops the actual execution
○ Deferred functions in F are executed
○ F returns to its caller
○ For the caller, F acts like a call to panic
○ Once all the functions of the panicking goroutine have returned, the program crashes
14. package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer fmt.Println("Defer in f")
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Defer in f
panic: 4
goroutine 1 [running]:
16. Recover
● A built-in function from the runtime
● It recovers from a panicking goroutine
● It can only be used in a deferred function
● Calling recover will
○ Return the value given to panic and resume normal execution if the calling goroutine is panicking
○ Return nil and do nothing otherwise
17. package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
20. Panic should always be
recovered by the top level
function in order to return an
error (catchable by the caller)
21. Gin web server
● Panic recovery is implemented in the default middleware
● This recover prevents Gin runtime from crashing because a handler function (defined by the
program that is using Gin framework) panics
○ Handler function is passed to Gin router
○ Client calls the handled endpoint
○ Middleware calls the handling function
○ Handling function panics
○ When returning, handling function acts as a call to panic for the middleware
○ Middleware recovers the panic and throw a 500 HTTP error
22. JSON package
● unmarshal function handles recovery
● value function calls the type associated
decoding function
● object, array and literal functions reads data
and stores it into given struct
○ literal is the simple function
○ object and array recalls value (indirect
recursion)
● On error, panic is used to unstack calls
23. Stop sending values to a closed channel
producer
consumer
consumer
consumer
channel
producer
produce
r
close
send
panic
producer
● Multi-producers channel is a problem
○ If a producer closes the channel, others
shouldn’t send (or they’ll panic)
● We can handle it by using multiple ways
○ Share a “done” channel between producers
○ Use a N(1-producer/1-consumer) pattern
○ Recover panic
24. The problem
1. Create a channel
2. Generate 3 producers
a. After a random waiting time, each producer writes to the chan
b. Then, each producer closes the channel
3. Generate one consumer
a. It consumes the channel until it’s closed
4. Main waits for all the producers and the consumer to exit
25. The solution
1. Create a channel
2. Generate 3 producers
a. After a random waiting time, each producer writes to the chan
b. Then, each producer closes the channel if nothing has been recovered
3. Generate one consumer
a. It consumes the channel until it’s closed
4. Main waits for all the producers and the consumer to exit
26. The solution
func main() {
ch := make(chan int)
wg := sync.WaitGroup{}
// Producers
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer func() {
if recover() == nil {
close(ch)
}
fmt.Printf("Producer exited: %dn", i)
wg.Done()
}()
<-time.After(time.Second * time.Duration(rand.Intn(3)))
ch <- i
}(i)
}
// Consumer
wg.Add(1)
go func() {
defer func() {
fmt.Println("Consumer exited")
wg.Done()
}()
for {
select {
case i, ok := <-ch:
if !ok {
return
}
fmt.Printf("Consumed: %dn", i)
}
}
}()
wg.Wait()
}