iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
1
Software Development

Go繁不及備載系列 第 17

# Day17 Go-拖延症候群 (defer)

Day17 Go-拖延症候群 (defer)

推遲、延後

defer 是延遲執行的意思。

A defer statement defers the execution of a function until the surrounding function returns.
推遲執行defer的敘述式,盡可能地拖拖拖等等等,直到目前所在的func打烊結束要回傳時才做事。

這種非得等到最後一刻才去做的精神,和大眾你我一樣很明顯都是拖延病患者,非常的親民。
像我大概就被植入了defer 發文.寫稿(IT鐵人賽)這樣子的程式。

【defer】

來看看只有兩行的程式。

func main() {
	defer fmt.Println("退出main才執行")

	fmt.Println("Hi")
}

/* result:
Hi
退出main才執行
*/

fmt.Println()

var a = 50

func main() {
	defer add_a()
	fmt.Println(a)
}

func add_a() {
	a += 100
}
/* result:
50
*/

https://play.golang.org/p/VRSQqf1_Bly

疑?奇怪,這樣子懶惰到叫人心寒的函式可以用在哪呢?
世上有哪間公司會雇用懶人嗎?

有的話請務必找我過去。

程式會用到defer主要是需要拖延的時效性,類似鬧鐘能做到提醒的作用。

在開燈的時候,
defer 最後出去的人要關燈

把讀檔與關檔寫在一起,這樣比較不容易忘記要關。

f, err := os.Open("test.txt")
if err != nil {
    return err
}
defer f.Close()

也常運用在關閉channel關閉DB之中。

db, err := sql.Open("mysql", "USER:PASSWORD@/DB")
	if err != nil {
		log.Fatalln(err)
	}
	defer db.Close()

一切看似非常地正常、優雅且美好。

【defer的一些小坑】

兩個defer執行的優先順序,
為了要貫徹拖延的行為,越早分派的任務要越晚達成才行 => 每日拖延(1/1)✔️。

【defer前後順序】

https://play.golang.org/p/IFgv-lt3Y7Z

func main() {
	defer print1()
	defer print2()
}

func print1() {
	fmt.Println("p1")
}

func print2() {
	fmt.Println("p2")
}

/* result:
p2
p1
*/

【修改 defer中的參數】

defer印出的值是多少,更動前的值?還是更動後?

func main() {
	assign2(50)
}

func assign2(a int) int {
	defer fmt.Println(a) // 任務交代下來的時候 a值是50,然後盡可能地拖延
	a = 100              // 老闆更動了a為100
	return a
}

/* result:
50
*/
func main() {
	assign1(50)
}

func assign1(a int) int {
	a = 100              // 老闆更動了a為100
	defer fmt.Println(a) // 任務交代下來的時候 a值是100,然後盡可能地拖延
	return a
}

/* result:
100
*/

全看上頭交代時的參數

https://play.golang.org/p/MYCoWj7Womg

The deferred call's arguments are evaluated immediately,
but the function call is not executed until the surrounding function returns.

【當 defer遇上 os.Exit()】

https://play.golang.org/p/2ihaISoyjbd

defer總想等到店裡打烊時再做行動,
但是如果直接倒店os.Exit..

func main() {
	defer func() {
		fmt.Println("我很懶,想等到退出func的時候再印東西")
	}()
	os.Exit(0) // func直接被砍了
}

/* result:

*/

【匿名函式裡面的defer】

func main() {
	fmt.Println(func1())
}

func func1() int {
	var a int
	defer func() {
		a = 100
	}()
	return a	// defer:『喔 要回傳a了喔,可是func還沒退出所以我不想做事,反正回上司也沒有規定要回傳哪個a,所以擺爛。』
}

/* result:
0
*/
func main() {
	fmt.Println(func2())
}

func func2() (a int) {
	defer func() {
		a = 100
	}()
	return a //defer:『蛤,要回傳了喔?雖然想擺爛,但上司一開始指名規定要回傳a,先趕一下進度好了。』
}

/* result:
100
*/

https://play.golang.org/p/AE9WS1XOGjo

『自始至終,就是想拖』—— Golang.Defer


上一篇
# Day16 我OK,你先GO (sync)
下一篇
# Day18 無言的省略號(...),以及目錄架Go
系列文
Go繁不及備載35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
阿展展展
iT邦好手 1 級 ‧ 2020-10-02 01:05:17

拖延症候群 XDDD
這很好用啦 XD

gjlmotea iT邦研究生 5 級 ‧ 2020-10-02 20:01:46 檢舉

哈哈/images/emoticon/emoticon37.gif

0
尾椎
iT邦新手 5 級 ‧ 2020-10-03 23:37:06

playground

請問為什麼 var 在匿名函式中會return 0呀? 有比較明確的原因嗎? 我試著print,a在離開函式之前也確實是100,為什麼最後在main裡面會收到0呢?

看更多先前的回應...收起先前的回應...
gjlmotea iT邦研究生 5 級 ‧ 2020-10-04 01:06:52 檢舉

我一開始也只是整理結果、不是很理解,
剛剛查了下,發現跟 Naked return(Named return)有關

https://stackoverflow.com/questions/37248898/how-does-defer-and-named-return-value-work
預先宣告、return回來的值居然是個坑呢,之後找時間補上/images/emoticon/emoticon13.gif
https://play.golang.org/p/puh6ijy-GVZ

尾椎 iT邦新手 5 級 ‧ 2020-10-04 22:25:53 檢舉

謝謝解惑,可以請教一下這個答案用什麼關鍵字找出來的嗎?我其實也有嘗試找答案,但不太會下關鍵字。

gjlmotea iT邦研究生 5 級 ‧ 2020-10-05 10:25:57 檢舉

一開始從匿名函式找 defer anonymous function,但發現結果不太一樣。後來從函式回傳值的預先宣告 return declare 找,才曉得這個種用法叫 Named return,然後再加defer來查

雷N iT邦研究生 1 級 ‧ 2020-10-12 13:29:00 檢舉

https://golang.org/doc/effective_go.html#defer
GO Defer這裡提到,
The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes.
再出現Defer時, 就會把當時的value傳入了. 並非等到defer被調用才看變數的值. 所以它倆其實不是同一個位子的變數

gjlmotea iT邦研究生 5 級 ‧ 2020-10-12 21:15:15 檢舉

當初有看到這一段,但不太能理解意思,
看到N大解釋之後更加明白了,
非常感謝

尾椎 iT邦新手 5 級 ‧ 2020-10-15 17:27:24 檢舉

是的,後來發現defer傳入pointer可能會是一個坑。

  1. https://play.golang.org/p/pcJ7QrQUBdT
  2. https://play.golang.org/p/EtoFgPZAID_w (with pointer)
0
flier268
iT邦新手 5 級 ‧ 2022-06-03 11:24:10

Go新手小實驗

Defer傳址
結論:func在呼叫前就會把parameter複製一遍放在一邊,所以如果是傳值(50)進去,會印出50,但如果傳址(&50)進去,則會印出100,因為被複製一遍的是地址

不知理解是否正確?

我要留言

立即登入留言