avatarPhuong Le (@func25)

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3271

Abstract

-keyword">type</span> Person <span class="hljs-keyword">struct</span> { Name <span class="hljs-type">string</span> }

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p Person)</span></span> SayHi() { fmt.Println(<span class="hljs-string">"Hi, my name is"</span>, p.Name) }

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { writer := Person{Name: <span class="hljs-string">"Joe"</span>} <span class="hljs-keyword">defer</span> writer.SayHi()

<span class="hljs-comment">// fix the wrong name</span> writer.Name = <span class="hljs-string">"Aiden"</span> }</pre></div><p id="835a">Now, this one’s different. See, <code>writer.SayHi()</code> has zero parameters, unlike our previous case.</p><p id="0647">So, what’s gonna show up on your screen this time?</p><p id="55ab">Even though <code>writer.SayHi()</code> comes in at the end, you'd expect it to say, <i>"Hi, my name is Aiden"</i> right?</p><p id="3fc5">But here’s the kicker, because <code>writer</code> gets locked in at the moment you call defer, it's actually gonna say, <i>"Hi, my name is Joe".</i></p><p id="2b21">You’ve seen how the <code>SayHi()</code> method uses a value receiver and this means the <code>defer</code> takes a quick snapshot of the <code>writer</code> variable when it kicks in. It’ll then run <code>SayHi()</code> on this <b>frozen copy, not the latest version</b>.</p><p id="055b">This can be dangerous when you want to do something with the object after you initialize or change it.</p><blockquote id="b342"><p><b>“Hey, will the solution from section one work here?”</b></p></blockquote><p id="2a01">Absolutely, it will.</p><p id="a02a">But hang on, there’s another way to get around this. You could swap out the value receiver for a pointer receiver, like this:</p><div id="019c"><pre><span class="hljs-comment">// Use *Person, not Person</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p *Person)</span></span> SayHi() { fmt.Println(<span class="hljs-string">"Hi, my name is"</span>, p.Name) }</pre></div><p id="4683">Just so we’re clear, this isn’t the ideal solution since using a pointer here is a bit extra since we’re not really changing the ‘person’ object. Consider this more of a learning point.</p><h1 id="a35e">3. Don’t worry about a panic, Defer’s got your back</h1><p id="50d0">When you use the ‘defer’ keyword, the corresponding function executes after the main function ends, even if there’s a panic along the way. This is particularly useful for doing some essential cleanup like shutting down files or releasing locks.</p><blockquote id="7337"><p><b>“So, can this help keep the service running?”</b></p></blockquote><p id="9535">You bet.</p><p id="16a5">It’s a common practice to pair <code>defer</code> with <code>recover</code> for this very reason, to manage unexpected panics and prevent service interruptions:</p><div id="2efd"><pre><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { <span class="hljs-keyword">defer</span> <span class="hljs-function"

Options

<span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { <span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r != <span class="hljs-literal">nil</span> { fmt.Println(<span class="hljs-string">"Recovered from panic:"</span>, r) } }()

<span class="hljs-built_in">panic</span>(<span class="hljs-string">"This is a panic!"</span>)

}

<span class="hljs-comment">// Recovered from panic: This is a panic!</span></pre></div><p id="af25">Web server libraries, such as gin and iris, are way ahead of you, they’ve got built-in middleware called <code>Recover</code> that leverages this very approach.</p><h1 id="058b">4. Defer can change the outcome of function</h1><p id="ac17">Suppose you’ve got named return values, like ‘x’ in the example that follows:</p><div id="2626"><pre><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deferMe</span><span class="hljs-params">()</span></span> (x <span class="hljs-type">int</span>) { x = <span class="hljs-number">1</span> <span class="hljs-keyword">return</span> }</pre></div><p id="2c6e">With a clever use of <code>defer</code>, you can actually change what the function returns before it’s done.</p><div id="b3ff"><pre><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { fmt.Println(deferMe()) <span class="hljs-comment">// 2</span> }

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">deferMe</span><span class="hljs-params">()</span></span> (x <span class="hljs-type">int</span>) { <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { x *= <span class="hljs-number">2</span> }()

x = <span class="hljs-number">1</span>

<span class="hljs-keyword">return</span> }</pre></div><p id="da39">But hold on, while this is a neat trick, be cautious.</p><p id="da02">Doing this can make your code tough to follow and even tougher to debug. So maybe think twice before you put this into your production code.</p><h1 id="4d9a">5. Defer order — Last In First Out</h1><p id="8e31">Remember, the execution of defer statements follows a last-in, first-out (LIFO) pattern. If you’ve lined up several defer statements, they’ll execute in reverse order to how you set them up.</p><p id="d4d9">Take a look at this example:</p><div id="25f2"><pre><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { <span class="hljs-keyword">defer</span> fmt.Println(<span class="hljs-string">"Defer 1"</span>) <span class="hljs-keyword">defer</span> fmt.Println(<span class="hljs-string">"Defer 2"</span>) <span class="hljs-keyword">defer</span> fmt.Println(<span class="hljs-string">"Defer 3"</span>) }

<span class="hljs-comment">// Defer 3</span> <span class="hljs-comment">// Defer 2</span> <span class="hljs-comment">// Defer 1</span></pre></div><p id="1f5b">Defer can do a lot of tricks, but that doesn’t mean you should use it for showing your skill.</p></article></body>

GO SECRET

Go Secret — Defer: What you know about DEFER in Go is not enough!

This article looks at some not-so-obvious things about the “defer” statement in Go, showing you a few things you might not know.

Photo by Jay Wennington on Unsplash

Hey, you think you know everything there is about ‘defer’? Well, think twice.

Believe me, it’s not just about postponing a function’s execution until your current function wraps up. Whether you’re new to Go or an old hand, this guide has something valuable for you.

So, let’s get started.

1. Evaluating Arguments

Have you ever realized that the arguments for a deferred function in Go are actually set when you first hit the defer statement, not when the deferred function finally runs?

Check out this example for clarity:

package main

import "fmt"

func main() {
  i := 0
  defer fmt.Println(i)
  i++
}

So, what do you think will pop up in the console?

Even though fmt.Println(i) only fires off at the end, after i is incremented to 1, it still outputs 0. Weird, isn't it?

That’s because i is evaluated at the moment the defer statement runs, and at that point, i is zero.

“So how do I work around this?”

Fixing this issue is actually quite easy, you simply move the argument into the deferred function’s body:

func main() {
 i := 0
 defer func() {
  fmt.Println(i)
 }()
 i++
}

// 1

“Already knew this, what’s next?”

If that didn’t catch you off guard, stay tuned, the next section dives deeper but with a twist in another Go feature.

2. Evaluating Function Receivers

You’ve got the hang of how deferred function arguments work, right? So, how about diving into function receivers and defer?

Check this sample:

package main

import "fmt"

type Person struct {
  Name string
}

func (p Person) SayHi() {
  fmt.Println("Hi, my name is", p.Name)
}

func main() {
  writer := Person{Name: "Joe"}
  defer writer.SayHi()
  
  // fix the wrong name
  writer.Name = "Aiden"
}

Now, this one’s different. See, writer.SayHi() has zero parameters, unlike our previous case.

So, what’s gonna show up on your screen this time?

Even though writer.SayHi() comes in at the end, you'd expect it to say, "Hi, my name is Aiden" right?

But here’s the kicker, because writer gets locked in at the moment you call defer, it's actually gonna say, "Hi, my name is Joe".

You’ve seen how the SayHi() method uses a value receiver and this means the defer takes a quick snapshot of the writer variable when it kicks in. It’ll then run SayHi() on this frozen copy, not the latest version.

This can be dangerous when you want to do something with the object after you initialize or change it.

“Hey, will the solution from section one work here?”

Absolutely, it will.

But hang on, there’s another way to get around this. You could swap out the value receiver for a pointer receiver, like this:

// Use *Person, not Person
func (p *Person) SayHi() {
  fmt.Println("Hi, my name is", p.Name)
}

Just so we’re clear, this isn’t the ideal solution since using a pointer here is a bit extra since we’re not really changing the ‘person’ object. Consider this more of a learning point.

3. Don’t worry about a panic, Defer’s got your back

When you use the ‘defer’ keyword, the corresponding function executes after the main function ends, even if there’s a panic along the way. This is particularly useful for doing some essential cleanup like shutting down files or releasing locks.

“So, can this help keep the service running?”

You bet.

It’s a common practice to pair defer with recover for this very reason, to manage unexpected panics and prevent service interruptions:

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()

    panic("This is a panic!")
}

// Recovered from panic: This is a panic!

Web server libraries, such as gin and iris, are way ahead of you, they’ve got built-in middleware called Recover that leverages this very approach.

4. Defer can change the outcome of function

Suppose you’ve got named return values, like ‘x’ in the example that follows:

func deferMe() (x int) {
  x = 1
  return
}

With a clever use of defer, you can actually change what the function returns before it’s done.

func main() {
  fmt.Println(deferMe()) // 2
}

func deferMe() (x int) {
  defer func() { x *= 2 }()

  x = 1
  
  return
}

But hold on, while this is a neat trick, be cautious.

Doing this can make your code tough to follow and even tougher to debug. So maybe think twice before you put this into your production code.

5. Defer order — Last In First Out

Remember, the execution of defer statements follows a last-in, first-out (LIFO) pattern. If you’ve lined up several defer statements, they’ll execute in reverse order to how you set them up.

Take a look at this example:

func main() {
 defer fmt.Println("Defer 1")
 defer fmt.Println("Defer 2")
 defer fmt.Println("Defer 3")
}

// Defer 3
// Defer 2
// Defer 1

Defer can do a lot of tricks, but that doesn’t mean you should use it for showing your skill.

Defer
Golang
Golang Tutorial
Golang Development
Recommended from ReadMedium