paint-brush
On Golang’s `defer` keywordby@tejasmanohar
2,272 reads
2,272 reads

On Golang’s `defer` keyword

by Tejas ManoharAugust 27th, 2016
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

<a href="https://golang.org/" target="_blank">Golang</a> has a <a href="https://blog.golang.org/defer-panic-and-recover" target="_blank"><em>defer</em> keyword</a>, which “defers” the execution of a function until the surrounding <a href="https://hackernoon.com/tagged/function" target="_blank">function</a> returns. Multiple <em>defer</em>s are stacked <a href="https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29" target="_blank">last-in first-out</a> so that the most recently <em>defer</em>red function is run first.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - On Golang’s `defer` keyword
Tejas Manohar HackerNoon profile picture

Golang has a defer keyword, which “defers” the execution of a function until the surrounding function returns. Multiple _defer_s are stacked last-in first-out so that the most recently _defer_red function is run first.

Let’s check out an example of this in practice.


func writeLogs(c chan []string) {defer stats.Duration("write_logs_fn", time.Now()) // order: 4





file, err := os.Create("request.log", filename)if err != nil {panic(err)}defer file.Close() // order: 3


writer := csv.NewWriter(file)defer writer.Flush() // order: 2








for line := range c {err := writer.Write(line)if err != nil {panic(err)}defer stats.Increment("wrote_line") // order: 1}}

At first glance, you may ask —

How is this different from the finally keyword in other languages?

From Java Docs,

finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

Well, let’s compare defer to the following Java example using finally.




static void writeLogs(List<List<String>> lines) throws Exception {Date start = new Date();FileWriter writer = null;int lineCount = 0;

















try {writer = new FileWriter("request.log");// non-buffered inputfor (int i = 0; i < lines.size(); i++) {// naive CSV writerfor (value : lines[i]) {writer.append(value);writer.append(",");}writer.append("\n");lineCount++;}} finally {if (writer != null) {writer.flush();writer.close();}

**stats.increment("wrote\_line", lineCount);  
stats.duration(start);  


}**}

Off the bat, you can identify a handful of “gotchas”:

  • Separation of similar concerns. finally separates alike concerns. Because you can’t declare that you’d like to close a file when you open it, you can’t effectively plan ahead; instead, you have to remember to fit it into the surrounding context. I can’t count how many programmer errors I’ve seen due to separating alike concerns.
  • Interruptions. finally requires immediate, exclusive, and interrupting attention, whereas defer allows you to plan ahead without interrupting your usual control flow. In the Go example, we could easily defer incrementing a metric as we read each line, whereas in Java, deferring multiple calls would require more advanced control flow, like a queue, so instead, we have to reorganize our flow by aggregating.
  • Readability. finally enforces nesting, which hurts overall readability, whereas defer can be dropped in inline. Notice the != null check, counters, and separate definition from declaration. Holistically, none of these characteristics are meaningful to a reader, but they’re forced to parse it due to the nested structure.
  • Imperative vs. Declarative. This may not be a point you were expecting to flow to Go’s side, but altogether, defer is more declarative. You tell the computer that you want to do X, Y, Z rather than how.

Alike most technical debt, I’ve found that each of these “gotchas” becomes more apparent as the complexity of your function grows.

Conclusion

Though unconventional, Go’s defer is pure brilliance. It disguises a generally complex control flow with a simple, universal one that both helps readability and keeps things declarative (two factors that are not mutually inclusive). It’s far superior to traditional teardown via finally, and you should almost always prefer it to rolling out your own control flow.

If you found this interesting, consider following me on Twitter, where I share my experiences with Go amongst other things.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising &sponsorship opportunities.

To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!