上一篇: https://hacpai.com/article/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
定义函数,左大括号依旧不能另起一行。
func test(x, y int, s string) (int, string) { // 类型相同的相邻参数可合并。 n := x + y // 多返回值必须用括号。 return n, fmt.Sprintf(s, n) }
函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。
有返回值的函数,必须有明确的终止语句,否则会引发编译错误。
3.2 变参
变参本质上就是 slice
。只能有一个,且必须是最后一个。
使用 slice
对象做变参时,必须展开。
func main() { s := []int{1, 2, 3} println(test("sum: %d", s...)) }
3.3 返回值
不能用容器对象接收多返回值。只能用多个变量,或 "_" 忽略。
func test() (int, int) { return 1, 2 } func main() { // s := make([]int, 2) // s = test() // Error: multiple-value test() in single-value context x, _ := test() println(x) }
多返回值可直接作为其他函数调用实参。
func test() (int, int) { return 1, 2 } func add(x, y int) int { return x + y } func sum(n ...int) int { var x int for _, i := range n { x += i } return x } func main() { println(add(test())) println(sum(test())) }
命名返回参数可看做与形参类似的局部变量,最后由 return
隐式返回。
func add(x, y int) (z int) { z = x + y return } func main() { println(add(1, 2)) }
命名返回参数可被同名局部变量遮蔽,此时需要显式返回。
func add(x, y int) (z int) { { // 不能在一个级别,引发 "z redeclared in this block" 错误。 var z = x + y // return // Error: z is shadowed during return return z // 必须显式返回。 } }
命名返回参数允许 defer
延迟调用通过闭包读取和修改。
func add(x, y int) (z int) { defer func() { z += 100 }() z = x + y return } func main() { println(add(1, 2)) // 输出: 103 }
显式 return
返回前,会先修改命名返回参数。
func add(x, y int) (z int) { defer func() { println(z) // 输出: 203 }() z = x + y return z + 200 // 执⾏行顺序: (z = z + 200) -> (call defer) -> (ret) } func main() { println(add(1, 2)) // 输出: 203 }
3.4 匿名函数
匿名函数可赋值给变量,做为结构字段,或者在 channel
里传送。
// --- function variable --- fn := func() { println("Hello, World!") } fn() // --- function collection --- fns := [](func(x int) int){ func(x int) int { return x + 1 }, func(x int) int { return x + 2 }, } println(fns[0](100)) // --- function as field --- d := struct { fn func() string }{ fn: func() string { return "Hello, World!" }, } println(d.fn()) // --- channel of function --- fc := make(chan func() string, 2) fc <- func() string { return "Hello, World!" } println((<-fc)())
闭包复制的是原对象指针,这就很容易解释延迟引用现象。
在汇编层面,test 实际返回的是 FuncVal 对象,其中包含了匿名函数地址、闭包对象指针。当调用匿名函数时,只需以某个寄存器传递该对象即可。
FuncVal { func_address, closure_var_pointer ... }
下一篇: https://hacpai.com/article/1438260619759
- 本系列是基于雨痕的《Go 学习笔记》(第四版)整理汇编而成,非常感谢雨痕的辛勤付出与分享!
- 转载请注明:文章转载自:黑客与画家的社区 [https://hacpai.com]
- 如果你觉得本章节做得不错,请在下面打赏一下吧~
社区小贴士
- 关注标签 [golang] 可以方便查看 Go 相关帖子
- 关注作者后如有新帖将会收到通知
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于