上一篇: [1438070631857]
ToC
- Go 边看边练 -《Go 学习笔记》系列(一)- 变量、常量
- Go 边看边练 -《Go 学习笔记》系列(二)- 类型、字符串
- Go 边看边练 -《Go 学习笔记》系列(三)- 指针
- Go 边看边练 -《Go 学习笔记》系列(四)- 控制流 1
- Go 边看边练 -《Go 学习笔记》系列(五)- 控制流 2
- Go 边看边练 -《Go 学习笔记》系列(六)- 函数
- Go 边看边练 -《Go 学习笔记》系列(七)- 错误处理
- Go 边看边练 -《Go 学习笔记》系列(八)- 数组、切片
- Go 边看边练 -《Go 学习笔记》系列(九)- Map、结构体
- Go 边看边练 -《Go 学习笔记》系列(十)- 方法
- Go 边看边练 -《Go 学习笔记》系列(十一)- 表达式
- Go 边看边练 -《Go 学习笔记》系列(十二)- 接口
- Go 边看边练 -《Go 学习笔记》系列(十三)- Goroutine
- Go 边看边练 -《Go 学习笔记》系列(十四)- Channel
3.1 函数定义
不支持 嵌套 (nested)、重载 (overload) 和 默认参数 (default parameter)。
- 无需声明原型。
- 支持不定 ⻓长变参。
- 支持多返回值。
- 支持命名返回参数。
- 支持匿名函数和闭包。
使用关键字 func
定义函数,左大括号依旧不能另起一行。
1func test(x, y int, s string) (int, string) { // 类型相同的相邻参数可合并。
2 n := x + y // 多返回值必须用括号。
3 return n, fmt.Sprintf(s, n)
4}
函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。
有返回值的函数,必须有明确的终止语句,否则会引发编译错误。
3.2 变参
变参本质上就是 slice
。只能有一个,且必须是最后一个。
使用 slice
对象做变参时,必须展开。
1func main() {
2 s := []int{1, 2, 3}
3 println(test("sum: %d", s...))
4}
3.3 返回值
不能用容器对象接收多返回值。只能用多个变量,或 "_" 忽略。
1func test() (int, int) {
2 return 1, 2
3}
4
5func main() {
6 // s := make([]int, 2)
7 // s = test() // Error: multiple-value test() in single-value context
8
9 x, _ := test()
10 println(x)
11}
多返回值可直接作为其他函数调用实参。
1func test() (int, int) {
2 return 1, 2
3}
4
5func add(x, y int) int {
6 return x + y
7}
8
9func sum(n ...int) int {
10 var x int
11 for _, i := range n {
12 x += i
13 }
14
15 return x
16}
17
18func main() {
19 println(add(test()))
20 println(sum(test()))
21}
命名返回参数可看做与形参类似的局部变量,最后由 return
隐式返回。
1func add(x, y int) (z int) {
2 z = x + y
3
4 return
5}
6
7func main() {
8 println(add(1, 2))
9}
命名返回参数可被同名局部变量遮蔽,此时需要显式返回。
1func add(x, y int) (z int) {
2 { // 不能在一个级别,引发 "z redeclared in this block" 错误。
3 var z = x + y
4 // return // Error: z is shadowed during return
5 return z // 必须显式返回。
6 }
7}
命名返回参数允许 defer
延迟调用通过闭包读取和修改。
1func add(x, y int) (z int) {
2 defer func() {
3 z += 100
4 }()
5
6 z = x + y
7 return
8}
9
10func main() {
11 println(add(1, 2)) // 输出: 103
12}
显式 return
返回前,会先修改命名返回参数。
1func add(x, y int) (z int) {
2 defer func() {
3 println(z) // 输出: 203
4 }()
5
6 z = x + y
7 return z + 200 // 执⾏行顺序: (z = z + 200) -> (call defer) -> (ret)
8}
9
10func main() {
11 println(add(1, 2)) // 输出: 203
12}
3.4 匿名函数
匿名函数可赋值给变量,做为结构字段,或者在 channel
里传送。
1// --- function variable ---
2
3fn := func() { println("Hello, World!") }
4fn()
5
6// --- function collection ---
7
8fns := [](func(x int) int){
9 func(x int) int { return x + 1 },
10 func(x int) int { return x + 2 },
11}
12
13println(fns[0](100))
14
15// --- function as field ---
16
17d := struct {
18 fn func() string
19}{
20 fn: func() string { return "Hello, World!" },
21}
22
23println(d.fn())
24
25// --- channel of function ---
26
27fc := make(chan func() string, 2)
28fc <- func() string { return "Hello, World!" }
29println((<-fc)())
闭包复制的是原对象指针,这就很容易解释延迟引用现象。
在汇编层面,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调用匿名函数时,只需以某个寄存器传递该对象即可。
1FuncVal { func_address, closure_var_pointer ... }
下一篇: [1438260619759]
- 本系列是基于雨痕的《Go 学习笔记》(第四版)整理汇编而成,非常感谢雨痕的辛勤付出与分享!
- 转载请注明:文章转载自:黑客与画家的社区 [http://symphony.b3log.org]
- 如果你觉得本章节做得不错,请在下面打赏一下吧~
社区小贴士
- 关注标签 [golang] 可以方便查看 Go 相关帖子
- 关注标签 [Go 学习笔记] 可以方便查看本系列
- 关注作者后如有新帖将会收到通知
该文章同步自 黑客派