1. 程式人生 > >5 Gotchas of Defer in Go (Golang) — Part I

5 Gotchas of Defer in Go (Golang) — Part I

#3 — Defer as a wrapper

Sometimes you need to defer with closures to be more practical or some other reasons I cannot guess right now. For example, to open a database connection, then run some queries and at the end to ensure that it gets disconnected.

Example

type database struct{}
func (db *database) connect() (disconnect func
()) {
fmt.Println("connect")
  return func() {
fmt.Println("disconnect")
}
}

Let’s run it

db := &database{}
defer db.connect()

fmt.Println("query db...")

Output

query db...
connect

Why it doesn’t work?

It doesn’t disconnect and it connects at the end, which is a bug. Only thing that happened here is that connect()

gets saved aside until the func ends and it doesn’t run.

Solution

func() {
db := &database{}
  close := db.connect()
defer close()

fmt.Println("query db...")
}

Now, db.connect() returns a func and we can use it with defer to disconnect from the database when the surrounding func ends.

Output

connect
query db...
disconnect

Bad Practice:

Although it’s a bad practice to do this but I want to show you how you can do it without a variable. So, I hope you can see how defer and more generally how Go works.

func() {
db := &database{}
  defer db.connect()()
  ..
}

This code is technically almost the same with the above solution. Here, the first parantheses is for connecting to the database (which happens immediately on defer db.connect()) and then the second parantheses is for defer to run the disconnecter func (the returned closure) when the surrounding func ends.

This happens because db.connect() creates a value which is a closure and then defer registers it. The value of db.connect() needs to be resolved first to be registered with defer. This is not directly related with defer however it may solve some of your gotchas you may have.