defer 介紹

defer 函數是Go語言中一個特殊的函數,會在程式區段結束後被呼叫,以便做最後的收尾動作。

func main() {
	defer log.Println("EDDYCJY.")
	log.Println("end.")
}

// 結果
2019/05/19 21:15:02 end.
2019/05/19 21:15:02 EDDYCJY.

// 範例二
func main() {
	func() {
		 defer log.Println("defer.EDDYCJY.")
	}()
	log.Println("main.EDDYCJY.")
}

2019/05/22 23:30:27 defer.EDDYCJY.
2019/05/22 23:30:27 main.EDDYCJY.

後進先出特性

func main() {
   for i := 0; i < 6; i++ {
	defer log.Println("EDDYCJY" + strconv.Itoa(i) + ".")
   }
   log.Println("end.")
}

2019/05/19 21:19:17 end.
2019/05/19 21:19:17 EDDYCJY5.
2019/05/19 21:19:17 EDDYCJY4.
2019/05/19 21:19:17 EDDYCJY3.
2019/05/19 21:19:17 EDDYCJY2.
2019/05/19 21:19:17 EDDYCJY1.
2019/05/19 21:19:17 EDDYCJY0.

用作異常處理

func main() {
	defer func() {
		if e := recover(); e != nil {
			log.Println("EDDYCJY.")
		}
	}()
	panic("end.")
}

// 2019/05/20 22:22:57 EDDYCJY.

Deferred nil func

如果 defer 指到 nil 函數,則會出現panic錯誤。

func() {
  var run func() = nil
  defer run()
  fmt.Println("runs")
}

輸出:

runs
panic: runtime error: invalid memory address or nil pointer dereference

Defer inside a loop

defer inside a looop


這種用法下,defer row.Close() 會在函數結束後才被執行,會造成不可預期的錯誤。
* 解法一:直接結束

func() {
   for {
      row, err := db.Query("select ...")
      if err != nil {
         ...
      }
      row.Close()
   }
}
  • 解法二:呼叫另一個函數
func() {
   for {
      func() {
         row, err := db.Query("select ...")
         if err != nil {
            ...
         }
         row.Close()
      }()
   }
}

基本上使用defer會比沒有使用執行速度快很多很多。所以建議大家使用defer。

Defer as a wrapper

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

  fmt.Println("query db...")
}
  • 輸出
connect
query db...
disconnect