113

Go Defer Simplified with Practical Visuals – Learn Go Programming

 6 years ago
source link: https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff?gi=1eb0831b746d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go Defer Simplified with Practical Visuals

Learn about Golang’s defer statement with various usage examples.

1*5JFV1poqV95qyXu36Qm9cQ.png

What is defer?

It takes a func and executes it just before* the surrounding func returns whether there is a panic or not.

1*9jvoL3xXkwydC7C-EMcfkg.png

1*FFqiVq-lvmbRxzT6gOCGlQ.png

Prints: “first” than “later”

1*_rdOI_SbL9m07QBcKMqz5Q.png

👉 Go doesn’t need destructors because it has no built-in constructors. This is a good balance.

👉 Defer resembles to finally” however the defer belongs to the surrounding “func” while the finally belongs to an exception “block”.

1*_rdOI_SbL9m07QBcKMqz5Q.png

👊 Bonus: See my comment here about the internals of defer if you’re curious about how it works. It’s actually been documented pragmatically as “it runs — after — the surrounding func returns”, however, there are some other inner details.

1*-OQBs5b4u65NRQM8aFukAw.png

Releasing acquired resources

Defer funcs are often used to release the acquired resources inside a func.

1*2gfaUL_WUKfwwR1m3Vynfg.png

The func closes the opened file whether there is an error or not on all returns — marked with the star.

1*FFqiVq-lvmbRxzT6gOCGlQ.png
1*-OQBs5b4u65NRQM8aFukAw.png

Save us from panic

Defer can recover from a panic to prevent the termination of a program if the panic emitted from the same goroutine.

1*H0luK0YxgVlSkXqFsyhSnw.png

1*FFqiVq-lvmbRxzT6gOCGlQ.png
1*_rdOI_SbL9m07QBcKMqz5Q.png

recover() returns the value provided to panic() which lets you decide what you’d do with it. You can also pass an error or other types of values to panic, then you can check whether the panic was caused by the value you’re looking for. More here.

1*-OQBs5b4u65NRQM8aFukAw.png

Deferred closure

A deferred func can be of any type of func. So, when used with an anonymous funcobviously — the func becomes aware of its surroundings.

Notice that it sees the latest state of the surrounding values, check out:

1*kx5BnZbPHudssmhbXRNudw.png

1*FFqiVq-lvmbRxzT6gOCGlQ.png

Understand how a defer func sees its context

1*-OQBs5b4u65NRQM8aFukAw.png

Params evaluation

Go runtime will save any passed params to the deferred func at the time of registering the defer— not when it runs.

1*_rdOI_SbL9m07QBcKMqz5Q.png

Example

Declare a dummy func that registers a deferred closure. It also uses a named result value “n” to increase the passed number for the second time:

func count(i int) (n int) {  defer func(i int) {
n = n + i
}(i) i = i * 2
n = i return
}

Let’s try:

count(10)// output: 30

What happened?

1*GpuBW-PeJt4LwumKJwUwGQ.png

Parse the visual following the numbers (on the left): 1, 2, 3.

1*FFqiVq-lvmbRxzT6gOCGlQ.png
1*_rdOI_SbL9m07QBcKMqz5Q.png

In some situations defer can help you to change the result value before the return by using the named result values as seen in the example.

1*-OQBs5b4u65NRQM8aFukAw.png

Multiple defers

Multiple defers are saved in a stack list. So, the last registered defer will run as the first. Beware: Using multiple defers may hinder the readability.

Example

1*_s3_Lf92bz7W_U5EZPQSOA.png

Output

first
last

1*FFqiVq-lvmbRxzT6gOCGlQ.png

To understand how multiple defers work

1*-OQBs5b4u65NRQM8aFukAw.png

Watch how it works

1*y7F1q1EutTP7zHZ6VTpSSw.gif

The animation loops

1*-OQBs5b4u65NRQM8aFukAw.png

Deferred methods

You can also use methods with defer. However, there’s a quirk. Watch.

Without pointers

type Car struct {
model string
}func (c Car) PrintModel() {
fmt.Println(c.model)
}func main() {
c := Car{model: "DeLorean DMC-12"} defer c.PrintModel() c.model = "Chevrolet Impala"
}

Output

DeLorean DMC-12

With Pointers

func (c *Car) PrintModel() {
fmt.Println(c.model)
}

Output

Chevrolet Impala

What’s going on?

1*pTk0c8mVLT-sHBASXKDkXw.png

Remember that the passed params to a deferred func are saved aside immediately without waiting for the deferred func to be run.

So, when a method with a value-receiver is used with defer, the receiver will be copied (in this case Car) at the time of registering and the changes to it wouldn’t be visible (Car.model). Because the receiver is also an input param and evaluated immediately to “DeLorean DMC-12” when it’s registered with the defer.

On the other hand, when the receiver is a pointer when it’s called with defer, a new pointer is created but the address it points to would be the same with the “c” pointer above. So, any changes to it would be reflected flawlessly.

1*FFqiVq-lvmbRxzT6gOCGlQ.png
1*-OQBs5b4u65NRQM8aFukAw.png

Alright, that’s all for now. Thank you for reading so far.

Let’s stay in touch:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK