函式讓你能把程式碼組成一個可重複使用的單位。
它由func關鍵字、函式名稱,以及放在括號中、以逗號分隔的 0 個或多個參數與型別組成。
所有參數都必須明確標註型別;參數沒有型別推斷。 參數沒有預設值,因此所有函式參數都是必填的。
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// One parameter
func PrintHelloName(name string) {
fmt.Println("Hello " + name)
}
相同型別的參數可以一起宣告,後面接一個型別宣告。
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
我們快速看一下兩個經常被混淆的詞:parameters和arguments。
函式參數是定義在函式簽章裡的名稱,例如上面PrintGreetingName函式中的greeting和name。
函式引數則是我們呼叫函式時,實際傳給函式參數的具體值。
例如在下面的範例中,"Hello"和"Katrina"就是傳給greeting和name參數的引數:
PrintGreetingName("Hello", "Katrina")
函式參數之後是 0 個或多個回傳值,這些回傳值也必須明確標註型別。
只有一個回傳值時直接寫出,多個回傳值則用括號包起來。
函式使用 return 關鍵字把值回傳給呼叫的程式碼。
一個函式裡可以有多個return敘述。
一旦執行到其中一個return敘述,函式的執行就會結束。
如果要從函式回傳多個值,它們以逗號分隔。
關於多個回傳值的慣用寫法,可以在連結的概念頁面中找到更多資訊。
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
呼叫函式時,要指定函式名稱,並在括號中為函式的每個參數傳入引數。
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
除了參數之外,回傳值也可以選擇性地命名。
如果使用了具名回傳值,不帶引數的return敘述就會回傳這些值。
這就是所謂的「裸」回傳。
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
釐清傳值和傳參考的概念也很重要。
首先,我們來釐清傳值。
在下面的範例中,當我們把變數val傳給MultiplyByTwo函式時,傳入的是val的副本。
因此,newVal的值更新為4,但原始的變數val仍然是2。
實際上,Go 本質上會複製原始值,所以只有這個副本(也就是v)會被函式修改。
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
嚴格來說,在 Go 中所有引數都是傳值,也就是說,會複製提供給函式的值或資料。
但如果你不想複製傳入函式的資料,而想在函式中修改資料,就應該使用pointers作為引數,也就是傳參考。
我們使用pointer來達成傳參考。
把pointer引數傳入函式,就能修改傳入函式的底層資料,而不只是對資料的副本操作。
目前只要知道,指標型別可以從函式簽章中型別前面的*認出來就夠了。
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
如果pointer的概念讓你感到困惑,別擔心。
課程大綱後面有一個專門的章節,可以幫助你精通指標。
請注意,slices和maps是上述規則的例外。
當我們把slice或map作為引數傳入函式時,即使型別中沒有明確的*,它們也會被當成指標型別處理。
這表示如果我們把 slice 或 map 傳入函式並修改其底層資料,這些變更會反映到原始的 slice 或 map 上。
在 Go 中,函式被視為一級公民,這讓它們非常強大。 圍繞函式還有許多其他概念,例如方法和匿名函式,你會在之後的學習旅程中遇到。