Go defer 去掉閉包函數(shù),靠譜嗎?
大家好,我是煎魚(yú)。
在 Go 語(yǔ)言里,defer 關(guān)鍵字是大家很愛(ài)用的。因?yàn)樗兄?defer+recover+panic 的組合拳打法,還有種各種 defer close 等常用場(chǎng)景。
這是 Go 語(yǔ)言開(kāi)發(fā)者必知必會(huì)的編程姿勢(shì)。
defer 常見(jiàn)用法
在語(yǔ)法上,Go defer 的代碼示例如下:
package main
import "fmt"
func main() {
defer fmt.Println("煎魚(yú)你好!")
fmt.Println("放學(xué)別走")
}
輸出結(jié)果:
放學(xué)別走
煎魚(yú)你好!
那 defer 在 Go 里的常見(jiàn)用法有哪些呢?首先是上文用到的,直接 defer + 函數(shù):
defer f()
其次是 defer+閉包的方式:
defer func() {
result := f()
// do something with result
}()
其他還有在面試題上常被考究的傳參變形:
func f1() int {
i := 1
defer func() {
i++
}()
...
}
func f2() int {
i := 1
defer func(i int) {
i++
}(i)
....
}
這些代碼看起來(lái),我們總是在對(duì) defer 做閉包的各種聲明和使用。defer 會(huì)不會(huì)就是和閉包天生一對(duì)?
新提案:defer 代碼塊
最近大家也在討論一個(gè)與之相關(guān)的 Go 提案《proposal: Go 2: deferred code blocks[1]》,由 @Damien Lloyd 提出,想看看有沒(méi)有機(jī)會(huì)把 defer 的新語(yǔ)法落地。
圖片
原作者在使用 defer 時(shí)也是經(jīng)常:
defer f()
但這樣就無(wú)法獲得返回值。最終要變成:
defer func() {
result := f()
// do something with result
}()
基于上述類(lèi)似的原因,想引入如下具有 defer 作用的代碼塊語(yǔ)法:
defer {
// 在封閉函數(shù)的末尾執(zhí)行此操作
}
在使用了 defer 關(guān)鍵字的函數(shù)最后執(zhí)行這整個(gè)代碼塊 {...}。代碼塊中的每一行將按順序運(yùn)行。
作者給出的代碼示例:
func fn() {
f, err := os.Create("eddycjy.txt")
if err != nil {
panic(err)
}
defer {
err := f.Close()
if err != nil {
panic(err)
}
}
}
在 fn 函數(shù),聲明了 defer {...},代碼塊內(nèi)是對(duì) f.Close 的兜底判斷和異常拋出。在函數(shù)結(jié)束后執(zhí)行這整個(gè)代碼塊。
反對(duì)的聲音
當(dāng)然,這看著似乎是比較美好的??雌饋?lái)原提案作者只是簡(jiǎn)化了 defer 是的閉包使用,調(diào)整了作用域的范圍。
但在社區(qū)內(nèi)其實(shí)遭受比較多的反對(duì)聲音。包含但不限于以下幾點(diǎn):
1、收益比不高:這個(gè)提案只是避免了 func() 和 () 等閉包聲明,但是卻要增加新的 defer 語(yǔ)法(語(yǔ)言語(yǔ)法更改會(huì)帶來(lái)高昂成本),這個(gè)變更的 ROI 不高。
2、破壞兼容性:原 defer 關(guān)鍵字調(diào)用總是會(huì)跟著函數(shù)的詞法調(diào)用,有良好的一致性。如果進(jìn)行修改,會(huì)產(chǎn)生新的隱晦,破壞一致性。也會(huì)對(duì)現(xiàn)有的許多工具(例如:靜態(tài)分析工具)產(chǎn)生影響,全要改。
3、作用域問(wèn)題:原本 defer func{}() 的代碼塊結(jié)構(gòu)下,你的代碼作用域都限于閉包函數(shù)下。而使用新的 defer {} 的結(jié)構(gòu),該返回和操作,是否會(huì)影響到外部函數(shù)的結(jié)果?(這是最有爭(zhēng)議的一點(diǎn),作者也比較前言不搭后語(yǔ),沒(méi)明確指明語(yǔ)法意思)
總結(jié)
一開(kāi)始乍一眼一看,感覺(jué)只是把 defer 關(guān)鍵字語(yǔ)句簡(jiǎn)化一下,好像特別好,省了幾個(gè)單詞。就像 if err != nil 也會(huì)有提要用 Rust 的 ? 等用法來(lái)替代的。
經(jīng)過(guò)社區(qū)網(wǎng)友們指出后,發(fā)現(xiàn)這里貓膩不少。一門(mén)已經(jīng)有 10+ 年的編程語(yǔ)言,還有 Go1 兼容性保障的。做出這類(lèi)帶作用域的提案變更,是有比較大的風(fēng)險(xiǎn)的。
同時(shí)對(duì)于 Go 工具鏈的影響,也是非常大的。一改,直接都完?duì)僮恿?。確實(shí)需要盡量深思。原作者完全沒(méi)提到。
該提案,我寫(xiě)的時(shí)候正在開(kāi)放 3 周等待意見(jiàn)收集。很神奇,沒(méi)更多的人說(shuō)話(huà),但提案的表情給了很多個(gè)不認(rèn)同。