return语句
package main
func f() int {
i := 5
defer func() {
i++
}()
return i
}
func f1() (result int) {
defer func() {
result++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
func main() {
println(f())
println(f1())
println(f2())
println(f3())
}
go build -gcflags '-l' -o foo foo.go
$ go tool objdump -s "main\.foo" foo
TEXT main.foo(SB) /Users/kltao/code/go/src/example/foo.go
bar.go:6 0x104ea70 48c744240801000000 MOVQ $0x1, 0x8(SP)
bar.go:6 0x104ea79 48c744241002000000 MOVQ $0x2, 0x10(SP)
bar.go:6 0x104ea82 c3 RET
其实return也不是原子操作,而是被拆成两步:
rval = xxx
defer_func
ret
如果在return中返回的变量是显示声明的,比如说func foo() (ret int) {},rval也就是ret。所以返回的函数执行效果如下:
//f
rval = i
i ++
ret
//f1
result = 0
defer // result ++
return
//f2
r = t
defer // t = t + 5
return
闭包
- Go 语言中的闭包就是在函数内引用函数体之外的数据,这样就会产生一种结果,虽然数据定义是在函数外,但是在函数内部操作数据也会对数据产生影响
- 匿名函数对 i 的调用就是闭包引用,i++ 会影响外面定义的 i 的值。而 bar() 中的匿名函数是变量拷贝,i++ 并不会修改外部 i 值
```go
func foo() {
i := 1
go func() {
}() time.Sleep(xxx) println(i) } // i = 2i ++
func bar() { i := 1 go func(i int) { i ++ }(i) time.Sleep(xxx) println(i) } // i = 1 ```
defer的使用
我们在日常的生活中日常的项目,defer可以让我们不需要显示地关闭文件句柄,网络连接等。特别是一些当函数出现异常或者错误,提前返回了。这种方式是非常高效的。
defer底层的实现了一个栈,先进后出。每次使用defer关键字,就创建一个defer对象,以链表的形式关联到当前的goroutine中。有新的defer对象就插入表头中,执行时从表头取,这就实现了一个栈。
可见defer的实现成本是比较高的,我们有空可以写一个benchmark测试一下。因此,当我们知道应该在哪里释放的资源的时候,应该显示地释放资源。
defer + panic
使用 defer 的最合适的场景我觉得应该是和 recover 结合使用,也就是说在你不知道的程序何时可能会 panic 的时候,才引入 defer + recover