在go中,乙個函式可以返回多個值。
乙個函式內部可以將另乙個有多返回值的函式作為返回值。
可以將乙個返回多引數的函式作為該函式的引數。
如果乙個函式將所有的返回值都顯示的變數名,那麼該函式的return語句可以省略運算元。這稱之為bare return。
func
add(a, b, c int
)(d, e int
)
上述**中,如果d,e如果沒有被修改,那麼就返回零值。
很難保證乙個函式能夠沒有錯誤的執行,所以一定要將可能預見的錯誤進行返回。
如果導致失敗的原因只有乙個,額外的返回值可以是乙個布林值,通常被命名為ok。
處理錯誤的五種策略
執行中的錯誤有很多種,對於不同的錯誤應該採取不同的策略
最常用的方式是傳播錯誤。也就是從底層函式不斷的返回,並且在這個過程中給錯誤加上一些資訊,確保錯誤最後傳回main函式的時候能夠分析出錯誤從**傳導出來的。
偶然錯誤,進行重試。如果錯誤的發生是偶然性的,或由不可預知的問題導致的。乙個明智的選擇是重新嘗試失敗的操作。在重試時,我們需要限制重試的時間間隔或重試的次數,防止無限制的重試。
程式無法繼續執行,輸出錯誤資訊並結束程式。需要注意的是,這種策略只應在main中執行。對庫函式而言,應僅向上傳播錯誤,除非該錯誤意味著程式內部包含不一致性,即遇到了bug,才能在庫函式中結束程式。
有時,我們只需要輸出錯誤資訊就足夠了,不需要中斷程式的執行。我們可以通過log包提供函式
直接忽略掉錯誤。用於即便有錯誤也不會影響程式的整體情況。
函式像其他值一樣,擁有型別,可以被賦值給其他變數,傳遞給函式,從函式返回。
函式型別的零值是nil。呼叫值為nil的函式值會引起panic錯誤
函式值之間是不可比較的,也不能用函式值作為map的key。
舉個簡單有趣的例子
func
add(x, y int
)int
func
sub(x, y int
)int
func
op(x, y int
, f func
(int
,int
)int
)int
func
main()
在上面函式op中f就是乙個函式值。當然函式值也是可以像下面一樣操作的。
func
square
(n int
)int
func
negative
(n int
)int
func
product
(m, n int
)int
f := square
fmt.
println(f
(3))
// "9"
f = negative
fmt.
println(f
(3))
// "-3"
fmt.
printf
("%t\n"
, f)
// "func(int) int"
f = product // compile error: can't assign func(int, int) int to func(int) int
匿名函式從名字上的意義來看就是沒有名字的函式。它的乙個重要的應用場景就是閉包:比如函式a中定義了匿名函式b,並將匿名函式b做引數返回,其中b可以對a中的變數進行操作。所以在函式a外部接收到b,便可以通過b來對a中變數進行操作。
// squares返回乙個匿名函式。
// 該匿名函式每次被呼叫時都會返回下乙個數的平方。
func
squares()
func()
int}
func
main()
乙個更難但更有趣的閉包:
func
a(x int)(
func
(int
)int
,func
(int
)int
) b :=
func
(i int
)int
return a, b
}func
main()
捕獲迭代變數
慮這個樣乙個問題:你被要求首先建立一些目錄,再將目錄刪除。在下面的例子中我們用函式值來完成刪除操作。下面的示例**需要引入os包。為了使**簡單,我們忽略了所有的異常處理。
var rmdirs [
]func()
for_
, d :=
range
tempdirs()
)}// ...do some work…
for_
, rmdir :=
range rmdirs
引數數量可變的函式稱為為可變引數函式。
可變引數一定要在函式引數列表中的最後乙個。
func
dsum
(d int
, args ...
int)
intreturn d * result
}func
main()
fmt.
println
(dsum(2
, arr...))
// 30
// 對於陣列可以利用切片生成切片
arr1 :=
[...
]int
fmt.
println
(dsum(2
, arr1[:]
...)
)// 30
}
特性:
func
main()
}
結果為:2,1,0.符合上面所述特性的第一點
不過使用defer有乙個問題,就是上面所提到的最後一點,可以看看下面的程式,嘗試下想想可能會發生的結果:
func
main()
()}for i:=
0; i<
3; i++
(i)// 傳參
}}
結果:
210
333
首先,在結果中輸出的前三個變數是第二個for的結果,後面是三個是第乙個for的結果。(不明白的回去看上面的例子,結合特性的第一點)
第乙個for之所以會輸出3個3,是因為這裡產生了閉包,使用的i是func外的變數i,所以位址並沒有改變。
而第二個for中定義的函式其實自己宣告了變數,所以將外部的變數i傳參給func的時候是進行了值拷貝,所以func內部的i和外部的i是兩個位址。
分析一下下面函式,看看是不是和自己預期的一樣:
func
main()
for i:=
0; i<
4; i++()
fs[i]
=func()
}for
_, f :=
range fs
}
結果:
closure i = 4
closure i = 4
closure i = 4
closure i = 4
closure i = 4
defer i = 3
closure i = 4
defer i = 2
closure i = 4
defer i = 1
closure i = 4
defer i = 0
go語言中使用panic來觸發錯誤。一旦觸發panic,之後所有的操作都會暫停。
funca(
)funcb(
)funcc(
)func
main()
結果如下:可以看到c函式並不會被執行
a
bpanic: panic in b
在觸發panic函式之前有defer函式了,那麼被defer的函式能夠在觸發panic之後正常執行完。
例如將上面的函式b改寫為以下:
funcb(
)()panic
("panic in b"
)}
結果如下:
a
bfunc in b
panic: panic in b
可以看到defer的匿名函式會在panic之前執行,c函式並不會被執行
在程式設計中,我們能夠預想到一些錯誤,這些錯誤能通過處理後能夠保證程式繼續正確執行而不會中斷,那麼可以使用recover,使即便觸發panic程式依然能夠繼續執行。
繼續修改上面的函式b:
funcb(
)}()
panic
("panic in b"
)}
上述**中, err是panic的內容,整個程式執行結果如下:
a
brecover from panic: panic in b
c
可以看到程式恢復正常執行,並且執行完整個流程了。 《Go語言聖經》學習筆記 2 程式結構(上)
package main import fmt func main const i 100 可以將常量 變數 定義在最後面上面程式是可以正常執行並輸出正確結果的。一級型別 全域性變數,函式 都可以定義在後面。變數宣告語法 var 變數名字 型別 表示式使用方式如下 func main 當 中的變數沒...
日常 Go語言聖經 匿名函式習題
go語言聖經 匿名函式 1.擁有函式名的函式只能在包級語法塊中被宣告,通過函式字面量 function literal 我們可繞過這一限制,在任何表示式中表示乙個函式值 2.通過這種方式定義的函式可以訪問完整的詞法環境 lexical environment 這意味著在函式中定義的內部函式可以引用該...
日常 Go語言聖經 匿名函式習題
go語言聖經 匿名函式 1.擁有函式名的函式只能在包級語法塊中被宣告,通過函式字面量 function literal 我們可繞過這一限制,在任何表示式中表示乙個函式值 2.通過這種方式定義的函式可以訪問完整的詞法環境 lexical environment 這意味著在函式中定義的內部函式可以引用該...