前面我們練習的函式都沒有傳回值,但是正常函式會有輸入值,然後再執行函式完成後,會有傳回值,特別的是 Go 語言中的傳回值可以是 0 ~ 多個 (一直在洗腦) 。
值 1, 值 2... := 函式名稱()
補充:
有傳回值的函式裡面要有 return 敘述,若是遺漏他,一樣是會報錯的。
以下範例我們根據書中自己定義的 fizzBuzz() 函式作為基底,並傳回走訪到的數字以及對應到的字串。
fizzBuzz() 函式的規則:
範例 1:
package main
import "fmt"
func main() {
for i := 1; i <= 15; i++ {
n, s := fizzBuzz(i)
fmt.Println("數字:", n, "對應字串:", s)
}
}
func fizzBuzz(i int) (int, string) {
switch {
case i % 15 == 0:
return i, "FizzBuzz"
case i % 5 == 0:
return i, "Buzz"
case i % 3 == 0:
return i, "Fizz"
}
return i, ""
}
範例 1(執行結果):
數字: 1 對應字串:
數字: 2 對應字串:
數字: 3 對應字串: Fizz
數字: 4 對應字串:
數字: 5 對應字串: Buzz
數字: 6 對應字串: Fizz
數字: 7 對應字串:
數字: 8 對應字串:
數字: 9 對應字串: Fizz
數字: 10 對應字串: Buzz
數字: 11 對應字串:
數字: 12 對應字串: Fizz
數字: 13 對應字串:
數字: 14 對應字串:
數字: 15 對應字串: FizzBuzz
有時候我們在很多地方重複呼叫一個函式,但或許每個地方需要的傳回值,並不一定跟本來函式的傳回值數量相同,這時可以便可以使用 底線 _ 來忽略傳回值。
以範例 1 作為例子,若我這時不需要印出前一個回傳值 (數字) ,那就可以寫成如範例二的寫法:
範例 2:
package main
import "fmt"
func main() {
for i := 1; i <= 15; i++ {
_, s := fizzBuzz(i) // 以底線 _ 來忽略回傳值
fmt.Println("對應字串:", s)
}
}
func fizzBuzz(i int) (int, string) {
switch {
case i % 15 == 0:
return i, "FizzBuzz"
case i % 5 == 0:
return i, "Buzz"
case i % 3 == 0:
return i, "Fizz"
}
return i, ""
}
範例 2(執行結果):
對應字串:
對應字串:
對應字串: Fizz
對應字串:
對應字串: Buzz
對應字串: Fizz
對應字串:
對應字串:
對應字串: Fizz
對應字串: Buzz
對應字串:
對應字串: Fizz
對應字串:
對應字串:
對應字串: FizzBuzz
補充:
若是如範例 2 相同使用短變數宣告賦值時, := 的左邊至少需要有一個實際的變數,若是上面兩個傳回值都用 _ 接收, Go 語言便會報以下錯誤訊息。
no new variables on left side of :=
又或者是有時需要讀取檔案時,檔案會傳回我們讀取的位元組數量以及淺在錯誤,但我們只需要知道讀取的檔案是否存在(查無檔案會傳入 error),不需要知道有多少位元組數,這時也可以把傳回的第一個參數用 _ 省略,如下:
_, err := file.Read(bytes)
補充:
在 Go 語言的函式慣例會以 error 作為第二個傳回值。
且對於可能傳回 error 的函式,應該要妥善處理 (做好錯誤處理) ,才可以避免意料外的後果發生。
在鐵人賽 Day 21 時,我們有介紹結構方法,雖然呼叫結構方法跟呼叫函式有些許不同,但其實結構方法也是函式的一種,所以參數以及傳回值的寫法都是用於我們前面函式介紹到的寫法呦~
在宣告 Go 語言函式時,我們若給傳回值加上變數名稱,就會在函式內建立該名稱的區域變數,作用範圍一樣存在於函式內,如此一來我們便可以在函式內對於變數賦值。
範例 3:
package main
import "fmt"
func printMyFavorite() (name string, age int) {
name = "奇犽" // name 變數已經在傳回值建立,故這邊直接使用 = 賦值
age = 12
return name, age
}
func main(){
fmt.Println(printMyFavorite()) // 印出執行 printMyFavorite() 的結果,也就是 return 的 name 跟 age
}
範例 3(執行結果):
奇犽 12
如果沒有在 return 敘述後面指定要傳回的變數, Go 語言便會自己將傳回值清單的變數傳回,這就是 naked returns ,也可以稱為 具名 return (named return) 。
範例 4:
package main
import "fmt"
func printMyFavorite() (name string, age int) {
name = "奇犽" // name 變數已經在傳回值建立,故這邊直接使用 = 賦值
age = 12
return // 沒有在 return 敘述後面指定要傳回的變數, 會自己將傳回值name, age
}
func main(){
fmt.Println(printMyFavorite()) // 印出執行 printMyFavorite() 的結果,也就是 return 的 name 跟 age
}
範例 4(執行結果):
奇犽 12
但若是再稍微長一些或是複雜一些的函式中,就不建議使用 naked returns ,因為會變得不好閱讀,以及讓人搞混傳回值。另外還有可能發生 變數遮蔽 (shadowing) 的問題。
範例 5:
package main
import "fmt"
func message() (message string, err error) {
message = "錯誤"
if message == "錯誤"{
err := fmt.Errorf("這是錯誤的訊息呦!")
return
}
return // 沒有在 return 敘述後面指定要傳回的變數, 會自己將傳回值name, age
}
func main(){
fmt.Println(message())
}
範例 5(執行結果):
./main.go:1092:9: err declared but not used
./main.go:1093:9: result parameter err not in scope at return
./main.go:1092:9: inner declaration of var err error
這是因為我們在傳回值已經宣告了 err 變數,但是在 if 敘述中又重新宣告和初始化 err 變數,會造成 變數遮蔽 (shadowing) 讓 Go 程式不知道現在到底是要 return 哪一個 err ,要修改只要明確寫上 return 敘述即可。
package main
import "fmt"
func message() (message string, err error) {
message = "錯誤"
if message == "錯誤"{
err := fmt.Errorf("這是錯誤的訊息呦!")
return message, err
}
return // 沒有在 return 敘述後面指定要傳回的變數, 會自己將傳回值name, age
}
func main(){
fmt.Println(message())
}
錯誤 這是錯誤的訊息呦!
今天介紹在 Go 語言中函式 (function) 的傳回值,那我們明天繼續學習參數不定的函式,明天見~~