Go 讀者提問:Go 函式返回值命名有存在的意義嗎?

煎魚發表於2022-03-23

大家好,我是煎魚。

在前兩週我們在這篇《你能答對這道 Go 題目嗎?超過 80% 的人都答錯了...》文章中,針對題目,有小夥伴提出瞭如下問題:

為此,今天我們就來了解一下 Go 函式的返回值命名的意義是什麼?

案例

函式 a 帶返回值,函式 b 是不帶返回值命名的例子。如下:

func a() (done func(), err error) {
 return func() { 
   print("aaa: done") 
 }, nil
}

func b() (func(), error) {
 return func() { 
   print("aaa: done") 
 }, nil
}

這是日常工作中最常見的,前兩年年我聽到有同學不知道有命名返回值的用法,也不知道函式結尾返回個 return 的意義是什麼,這有啥用?

官方解釋

在官方的《Effective Go》中,明確將帶命名的返回值引數定義為 Named result parameters(命名的結果引數)。

函式簽名如下:

func nextInt(b []byte, pos int) (value, nextPos int) {

帶有三個明確的特徵:

  1. Go 函式的返回或結果 "引數 "可以被命名並作為常規變數使用,就像傳入引數一樣。
  2. 當命名時,它們在函式開始時被初始化為其型別的零值。
  3. 如果函式執行沒有引數的返回語句,結果引數的當前值被用作返回值。

簡單來講,就是和常規入參一樣,宣告後會自帶零值,如果 return 沒指定,則會預設返回宣告的返回變數。

官方定義的作用是:“可以使程式碼更短、更清晰”。像是給 nextInt 函式的返回的結果命名,那你就能很明確知道,第一個返回的值是 value,第二個是 nextPos,起到顯式宣告的作用。

建議

在官網的《A Tour of Go》中明確指出 Named result parameters 只建議在較短的函式中使用。

例如以下例子:

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

如果是在較長的函式中使用,它們會損害可讀性。也見過因為寫的太長了,為了優化可讀性,不斷換行的程式碼編寫,越搞越繁瑣的場景。

總結

實際上帶命名的返回引數,比較帶有 Go 的風格,就是顯式命名了返回。

但也會帶來可能存在的函式內返回的省略,以至於很多人新入門的朋友看不懂。又或是像是開頭文章內所介紹的,帶命名的返回引數寫著寫著變成遞迴函式,一手抖也是會出現的。

該特性,建議平時斟酌使用,短小精悍的可以考慮,又長又多的建議還是不要增加過多的繁瑣了。

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

推薦閱讀

相關文章