函式
函式宣告
函式生成包含函式的名字,形參列表,返回值列表(可選)以及函式體構成.
func name(parameter-list) (result-list) {
body
}
複製程式碼
需要注意一下幾點:
-
函式的形參列表和返回值列表組成函式的簽名,函式的簽名會在函式被呼叫的時候做校驗是否呼叫合法.
-
引數的傳遞是按值傳遞的.
-
支援多返回值.
-
函式變數是有型別的,不符合函式簽名型別的呼叫會報錯
func changeArr(a [3]int) { a[0] = 100 } func getArr(a [3]int) (int, int) { return a[0], a[1] // 多返回值 } func main() { test := [3]int{1,2,3} changeArr(test) fmt.Println(test[0]) // 1 陣列是基本型別 值傳遞不會改變原陣列 a, b := getArr(test); // 1, 2 } // 當形參的基本型別的時候,不會修改外部的值.當形參是引用型別的時候,有可能會修改外部的值. 複製程式碼
變長函式宣告
通過在引數列表最後的型別名稱之前使用省略號來宣告一個變長函式宣告.
func log(vals ...int) {
for _, value := range vals {
fmt.Println(value)
}
}
func main() {
b := []int{1,2,3}
log(b...) // 1, 2, 3
log(1,2,3) // 1, 2, 3
}
複製程式碼
函式變數(匿名函式)
通過在func關鍵字後不指定函式的名字可以宣告函式變數,這種方式函式能獲取整個詞法環境(外部的變數).
func add() func() int {
var x int;
return func() int {
x++
return x
}
}
func main() {
f := add()
fmt.Println(f()) // 1
fmt.Println(f()) // 2
}
複製程式碼
錯誤處理機制
go語言通過普通的值來報告錯誤.常規的錯誤是開發者可以預見並且決定錯誤的行為的.這樣得到的錯誤資訊由於沒有相應的堆疊資訊而更加清晰.
錯誤傳遞
呼叫者在呼叫函式發生錯誤的時候,在錯誤資訊上新增更多的呼叫資訊傳遞給上層.
func test2()([]int, error) {
return nil, errors.New("test2")
}
func test1() ([]int, error) {
ret, err := test2()
if err != nil {
return nil, fmt.Errorf("test1 call test2 %v", err)
}
return ret, nil;
}
func main() {
_, err := test1()
fmt.Println(err); // test1 call test2 test2
}
複製程式碼
defer
defer語句是普通的函式呼叫,defer語句能確保函式的return語句或函式執行完畢之後執行對應的defer函式.
func log() func() {
fmt.Println("start")
return func() { fmt.Println("end") }
}
func main() {
defer log()()
fmt.Println("test defer")
}
// 輸出 start test defer end
複製程式碼
注意點
-
defer執行匿名函式會獲取當前的詞法環境,有可能修改函式執行的結果.
-
defer語句能保證函式執行完執行,某些情況會導致資源無法釋放.
func readFills (filenams []string) { for _, filename := range filenams { f, err := os.Open(filename) defer f.Close() } } // 上面的例子會導致檔案描述符被消耗無法釋放,可以在進行單獨的封裝來控制defer對資源的釋放. 複製程式碼
方法
方法宣告
方法是宣告特定型別(物件)上可以執行的函式. 通常可以使用如下的方式宣告:
func (p structName) funcName(parameter-list) (result-list) {
body
}
// p 特定的型別(接受者) 宣告可以在p型別上呼叫funcName的方法
複製程式碼
注意:
- 由於方法的呼叫是p.funcName和獲取p結構體上的屬性一致,要注意同一型別上的命名衝突.
指標接收者方法
由於方法會複製實參,當需要方法的呼叫對外界產生影響的時候,就需要通過指標型別來完成方法的宣告,如下面的例子:
type Point struct {
x, y int
}
func (p *Point) ScaleBy(factor int) {
p.x *= factor
p.y *= factor
}
func main() {
p := &Point{10, 10} // 獲取指標
p.ScaleBy(2) // p{20, 20}
q := Point{1,2}
q.ScaleBy(3) // q{3,6} 當型別符合的時候,會進行隱式轉換 相當於 (&q).ScaleBy(3)
}
複製程式碼
方法變數和方法表示式
方法變數
可以將一個特定型別的方法賦值給一個變數,這個變數稱為方法變數.該方法變數已繫結到特定的接收者上(caller),通過傳遞形參就可以完成方法的呼叫.通常用於繫結特定的接受者.
type Point struct {
x, y int
}
func (p *Point) ScaleBy(factor int) {
p.x *= factor
p.y *= factor
}
func main() {
p := &Point{10, 10}
scaleBy := p.ScaleBy
scaleBy(2) // p{20, 20}
}
複製程式碼
方法表示式
type Point struct {
x, y int
}
func (p *Point) ScaleBy(factor int) {
p.x *= factor
p.y *= factor
}
func main() {
p := &Point{10, 10}
scaleBy := (*Point).ScaleBy // 方法表示式
scaleBy(p,2)
}複製程式碼