1. 程式人生 > >瞭解golang的不定引數(... parameters),這一篇就夠了

瞭解golang的不定引數(... parameters),這一篇就夠了

在實際開發中,總有一些函式的引數個數是在編碼過程中無法確定的,比如我們最常用的fmt.Printf和fmt.Println:

fmt.Printf("一共有%v行%v列\n", rows, cols)
fmt.Println("共計大小:", size)

當你需要實現類似的介面時,就需要我們的不定引數出場了。

golang的不定引數

顧名思義,不定引數就是一個佔位符,你可以將1個或者多個引數賦值給這個佔位符,這樣不管實際引數的數量是多少,都能交給不定引數來處理,我們看一下不定引數的宣告:

func Printf(format string, a ...interface{}) (n int
, err error) func Println(a ...interface{}) (n int, err error)

不定引數使用name ...Type的形式宣告在函式的引數列表中,而且需要是引數列表的最後一個引數,這點與其他語言類似;

不定引數在函式中將轉換為對應的[]Type型別,所以我們可以像使用slice時一樣來獲取傳給函式的引數們;

有一點值得注意,golang的不定引數不需要強制繫結引數的出現。

舉個例子,我想在c語言中實現一個求和任意個整數的函式sum:

int sum(int num, ...) {
    // todo
}

我們只有先指定至少一個非不定引數的形參(num)才能使用...不定引數,在golang中是不需要這樣做的:

func sum(nums ...int) int {
    //todo
}

這也是golang語法簡潔的其中一個體現。

傳遞引數給...不定引數

傳遞引數給帶有不定引數的函式有兩種形式,第一種與通常的引數傳遞沒有什麼區別,拿上一節的sum舉個例子:

sum(1, 2, 3)
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

除了引數的個數是動態變化的之外和普通的函式呼叫是一致的。

第二種形式是使用...運算子變數...的形式進行引數傳遞,這裡的變數必須是與不定引數型別相同的slice,而不能是其他型別(沒錯,陣列也不可以),看個例子:

numbers := []int
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} sum(numbers...) // 和sum(1, 2, 3, 4, 5, 6, 7, 8, 9. 10)等價

這種形式最常用的地方是在內建函式append裡:

result := []int{1, 3}
data := []int{5, 7, 9}
result = append(result, data...) // result == []int{1, 3, 5, 7, 9}

是不是和python的解包操作很像,沒錯,大部分情況下你可以把...運算子當做是golang的unpack操作,不過有幾點不同還是要注意的:

第一,只能對slice型別使用...運算子:

arr := [...]int{1, 2, 3, 4, 5}
sum(arr...) // 編譯無法通過

你會見到這樣的報錯資訊:cannot use arr (type [5]int) as type []int in argument to sum

這是因為不定引數實際是個slice,...運算子是個語法糖,它把前面的slice直接複製給不定引數,而不是先解包成獨立的n個引數再傳遞,這也是為什麼我只說...運算子看起來像unpack的原因。

第二個需要注意的地方是不能把獨立傳參和...運算子混用,再看個例子:

slice := []int{2, 3, 4, 5}
sum(1, slice...) // 無法通過編譯

 這次你會見到一個比較長的報錯:

too many arguments in call to sum
    have (number, []int...)
    want (...int)

這是和前面所說的原因是一樣的,...運算子將不定引數直接替換成了slice,這樣就導致前一個獨立給出的引數不再算入不定引數的範圍內,使得函式的引數列表從(...int)變成了(int, ...int),最終使得函式型別不匹配編譯失敗。

正確的做法也很簡單,不要混合使用...運算子給不定引數傳參即可。

讀了這篇文章,再加上一些簡單的聯絡,我相信你們一定也能掌握golang不定引數的使用。

參考:

https://golang.org/ref/spec#Passing_arguments_to_..._parameters

https://golang.org/doc/effective_go.html#append