匿名函数
package main
import "fmt"
func main() {
f := func() {
fmt.Println("test")
}
f()
}
闭包
下面代码中,输出的三个x的地址一定是一样的,它们引用同一个变量。
package main
import "fmt"
func main() {
f := closure(10)
fmt.Println(f(1))
fmt.Println(f(2))
}
func closure(x int) func(int) int {
fmt.Println(&x)
return func(y int) int {
fmt.Println(&x)
return x + y
}
}
/* possible output:
0xc000060068
0xc000060068
11
0xc000060068
12
*/
defer
- 执行方式类似其它语言中的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个执行
- 即使函数发生严重错误也会执行
- 支持匿名函数的调用
- 常用于资源清理、文件关闭、解锁以及记录时间等操作
- 通过与匿名函数配合可在return之后修改函数计算结果
- 如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址
下面这个代码中,defer时i
就传值进去了。所以输出210
。
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
defer fmt.Print(i)
}
}
下面这个代码中,由于闭包中的i
是对main
函数中局部变量i
的引用。defer
在main
函数结束后执行,而main
函数结束时i
的值已经为3。所以输出3次3
。
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i)
}()
}
}
下面这个代码中,闭包里每一次传递的string
事实上都不是同一个。所以输出的是ans2 ans1 ans0
。
package main
import (
"fmt"
"strconv"
)
func main() {
for i := 0; i < 3; i++ {
var t string = "ans" + strconv.Itoa(i)
defer func() {
fmt.Print(t + " ")
}()
}
}
defer配合panic与recover
package main
import "fmt"
func main() {
f1()
f2()
f3()
}
func f1() {
fmt.Println("func f1")
}
func f2() {
// defer 一定要在 panic 之前, 因为 panic 触发时
// panic 所在函数就会立刻终止并倒序调用所有已经存在的defer
// 若 defer 在 panic 之后, 程序根本不会知道后面有 defer
defer func() {
if err := recover(); err != nil {
fmt.Println("err =", err)
fmt.Println("recover in f2 (first)")
}
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("err =", err)
fmt.Println("recover in f2 (second)")
}
}()
panic("panic in f2")
}
func f3() {
fmt.Println("func f3")
}
/* output:
func f1
err = panic in f2
recover in f2 (second)
func f3
*/