使用 defer 時可能遇到的若干陷阱

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。go 的 defer 語句對改善代碼可讀性起了很大作用。但是,某些情況下 defer 的行為很容易引起混淆,並且難以快速理清。儘管作者已經使用 go 兩年多了,依然會被 defer 弄得撓頭不已。我的計劃是把過去曾困惑過我的一系列行為匯總起來,作為對自己的警示。## defer 的範圍是一個函數,不是一個語句塊一個變數只存在於一個語句塊的範圍內。 defer 語句所在的語句塊只會在函數返回時執行。我不清楚背後的原理,但是,如果你在一個迴圈裡面先分配資源,再用 defer 回收資源,就有可能帶來猝不及防的災難後果。```gofunc do(files []string) error {for _, file := range files {f, err := os.Open(file)if err != nil {return err}defer f.Close() // 這是錯誤用法!!// 使用 f}}```(譯者註:上述代碼會造成迴圈結束後才開始回收資源,而不是執行了一次迴圈就回收一次資源)## 方法鏈如果你在一個 defer 語句中鏈式調用方法,那麼除了最後一個函數以外其餘函數都會在調用時直接執行。 defer 要求一個函數作為 “參數” 。```gotype logger struct {}func (l *logger) Print(s string) {fmt.Printf("Log: %v\n", s)}type foo struct {l *logger}func (f *foo) Logger() *logger {fmt.Println("Logger()")return f.l}func do(f *foo) {defer f.Logger().Print("done")fmt.Println("do")} func main() {f := &foo{l: &logger{},}do(f)}```輸出結果——```Logger()doLog: done```Logger() 函數在 do() 函數之前就已經執行了。## 函數參數嗯,那麼如果最後一個函數接收了一個參數,結果又會怎麼樣呢? ? 按照常理來說 ,如果它是在外圍函數返回後才執行,那麼對變數的任何修改都會被捕獲到,事實真的如我們所料嗎?```gotype logger struct {}func (l *logger) Print(err error) {fmt.Printf("Log: %v\n", err)}type foo struct {l *logger}func (f *foo) Logger() *logger {fmt.Println("Logger()")return f.l}func do(f *foo) (err error) {defer f.Logger().Print(err)fmt.Println("do")return fmt.Errorf("ERROR")} func main() {f := &foo{l: &logger{},}do(f)}```猜猜輸出結果是?```Logger()doLog: <nil>```err 的值仍然是調用 defer 時候的值。任何對這個變數的修改都不會被 defer 語句捕獲,因為它們並不指向同一個值。## 針對非指標類型調用函數我們已經看到了 defer 語句中鏈式方法的執行特點。進一步深入下去,如果被調用的方法並不是定義在一個指標類型上,那麼將會在 defer 語句中複製出一個新的執行個體。```gotype metrics struct {success boollatency time.Duration}func (m metrics) Log() {fmt.Printf("Success: %v, Latency: %v\n", m.success, m.latency)}func foo() {var m metricsdefer m.Log()start := time.Now()// Do somethingtime.Sleep(2*time.Second)m.success = truem.latency = time.Now().Sub(start)}```輸出結果```Success: false, Latency: 0s```當 defer 語句執行時 m 被複製。 m.Foo() 實質是 Foo(m) 的簡寫形式。## 結論如果你使用 go 的時間足夠久,那麼這些可能都算不上 “陷阱” 。但對新手來說, defer 語句的很多地方都不符合 [最少吃驚原則](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) 。還有 [更](http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/) [多](https://studygolang.com/articles/12061) [地](https://studygolang.com/articles/12136) [方](https://studygolang.com/articles/12319) 深入研究了使用 go 時可能遇到的常見失誤。歡迎閱讀。

via: https://medium.com/@i0exception/some-common-traps-while-using-defer-205ebbdc0a3b

作者:Aniruddha 譯者:sunzhaohao 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

402 次點擊  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.