在Go語言中,函數是一等(first-class)類型。這意味着,咱們能夠把函數做爲值來傳遞和使用。函數表明着這樣一個過程:它接受若干輸入(參數),並通過一些步驟(語句)的執行以後再返回輸出(結果)。特別的是,Go語言中的函數能夠返回多個結果。
函數類型的字面量由關鍵字func
、由圓括號包裹參數聲明列表、空格以及能夠由圓括號包裹的結果聲明列表組成。其中,參數聲明列表中的單個參數聲明之間是由英文逗號分隔的。每一個參數聲明由參數名稱、空格和參數類型組成。參數聲明列表中的參數名稱是能夠被統一省略的。結果聲明列表的編寫方式與此相同。結果聲明列表中的結果名稱也是能夠被統一省略的。而且,在只有一個無名稱的結果聲明時還能夠省略括號。示例以下:安全
func(input1 string ,input2 string) string
這一類型字面量表示了一個接受兩個字符串類型的參數且會返回一個字符串類型的結果的函數。若是咱們在它的左邊加入type
關鍵字和一個標識符做爲名稱的話,那就變成了一個函數類型聲明,就像這樣:併發
type MyFunc func(input1 string ,input2 string) string
函數值(或簡稱函數)的寫法與此不徹底相同。編寫函數的時候須要先寫關鍵字func
和函數名稱,後跟參數聲明列表和結果聲明列表,最後是由花括號包裹的語句列表。例如:app
func myFunc(part1 string, part2 string) (result string) { result = part1 + part2 return }
咱們在這裏用到了一個小技巧:若是結果聲明是帶名稱的,那麼它就至關於一個已被聲明但未被顯式賦值的變量。咱們能夠爲它賦值且在return
語句中省略掉須要返回的結果值。至於什麼是return
語句,我就不用多說了吧。顯然,該函數還有一種更常規的寫法:函數
func myFunc(part1 string, part2 string) string { return part1 + part2 }
注意,函數myFunc
是函數類型MyFunc
的一個實現。實際上,只要一個函數的參數聲明列表和結果聲明列表中的數據類型的順序和名稱與某一個函數類型徹底一致,前者就是後者的一個實現。請你們回顧上面的示例並深入理解這句話。
咱們能夠聲明一個函數類型的變量,如:ui
var splice func(string, string) string // 等價於 var splice MyFunc
而後把函數myFunc
賦給它:atom
splice = myFunc
如此一來,咱們就能夠在這個變量之上實施調用動做了:spa
splice("1", "2")
實際上,這是一個調用表達式。它由表明函數的標識符(這裏是splice
)以及表明調用動做的、由圓括號包裹的參數值列表組成。
若是你以爲上面對splice
變量聲明和賦值有些囉嗦,那麼能夠這樣來簡化它:code
var splice = func(part1 string, part2 string) string { return part1 + part2 }
在這個示例中,咱們直接使用了一個匿名函數來初始化splice
變量。顧名思義,匿名函數就是不帶名稱的函數值。匿名函數直接由函數類型字面量和由花括號包裹的語句列表組成。注意,這裏的函數類型字面量中的參數名稱是不能被忽略的。
其實,咱們還能夠進一步簡化——索性省去splice
變量。既然咱們能夠在表明函數的變量上實施調用表達式,那麼在匿名函數上確定也是可行的。由於它們的本質是相同的。後者的示例以下:orm
var result = func(part1 string, part2 string) string { return part1 + part2 }("1", "2")
能夠看到,在這個匿名函數以後的便是表明調用動做的參數值列表。注意,這裏的result
變量的類型不是函數類型,而與後面的匿名函數的結果類型是相同的。
最後,函數類型的零值是nil
。這意味着,一個未被顯式賦值的、函數類型的變量的值必爲nil
。字符串
案例>
package main import ( "fmt" "strconv" "sync/atomic" ) // 員工ID生成器 type EmployeeIdGenerator func(company string, department string, sn uint32) string // 默認公司名稱 var company = "Gophers" // 序列號 var sn uint32 // 生成員工ID func generateId(generator EmployeeIdGenerator, department string) (string, bool) { // 這是一條 if 語句,咱們會在下一章講解它。 // 若員工ID生成器不可用,則沒法生成員工ID,應直接返回。 if generator == nil { return "", false } // 使用代碼包 sync/atomic 中提供的原子操做函數能夠保證併發安全。 newSn := atomic.AddUint32(&sn, 1) return generator(company, department, newSn), true } // 字符串類型和數值類型不可直接拼接,因此提供這樣一個函數做爲輔助。 func appendSn(firstPart string, sn uint32) string { return firstPart + strconv.FormatUint(uint64(sn), 10) } func main() { var generator EmployeeIdGenerator fmt.Println(generateId(generator, "RD")) }