go的defer和return的执行顺序
go的defer和return是golang中的两个关键字,return用于返回函数的返回值,也可以参与一定的流程控制,比如下面代码,return短路了后面的输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
// defer 和 return的详解
func main() {
foo(2)
foo(1)
}
func foo(i int) {
fmt.Println(i)
if i == 1 {
return
}
fmt.Println(i + 1)
}
|
结果:
2
3
1
第一次输出完整的输出了i和i+1,第二次输出被短路,只输出了1
defer是golang中的延迟调用,经常用于文件流的关闭,锁的解锁操作,defer后面的操作会在当前函数或者goroutine结束之后进行调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
// defer 和 return的详解
func main() {
foo()
}
func foo() {
defer fmt.Println("println defer")
fmt.Println("println foo")
}
输出:
println foo
println defer
|
defer自身有一些特性,比如defer和defer之间的执行顺序是先进后出,先defer的最后执行,分析下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
// defer 和 return的详解
func main() {
foo()
}
func foo() {
defer fmt.Println("floor 3")
defer fmt.Println("floor 2")
fmt.Println("floor 1")
}
输出:
floor 1
floor 2
floor 3
|
根据这一特性,如果我们defer调用的代码中存在panic 的可能性,为了保证系统的运行,我们应该在前面recover而不是后面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
ackage main
import "fmt"
// defer 和 return的详解
func main() {
foo()
}
func foo() {
defer func() {
panic("panic test")
}()
defer func() {
if err := recover(); err != nil {
fmt.Println("catch panic:", err)
}
}()
}
输出:
panic: panic test
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import "fmt"
// defer 和 return的详解
func main() {
foo()
}
func foo() {
defer func() {
if err := recover(); err != nil {
fmt.Println("catch panic:", err)
}
}()
defer func() {
panic("panic test")
}()
}
输出:
catch panic: panic test
|
defer和return的相互影响
defer和return的相互影响,主要是在返回值上表现,考虑下面代码,输出应该是什么:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import "fmt"
// defer 和 return的详解
func main() {
fmt.Println(foo1())
fmt.Println(foo2())
fmt.Println(foo3())
}
func foo1() int {
i := 1
defer func() { i++ }()
return i
}
func foo2() (i int) {
i = 1
defer func() { i++ }()
return i
}
func foo3() (i int) {
defer func() { i++ }()
return 1
}
|
输出:
1
2
2
导致上面情况的原因是
在 foo1 函数中,defer 语句中的闭包会在函数返回后执行,但是此时返回值已经确定为 1 ,所以最终返回 1 。
在 foo2 函数中,使用了命名返回值 i 。defer 语句中的闭包修改的是这个命名返回值,所以返回 2 。
在 foo3 函数中,同样使用了命名返回值 i ,defer 语句中的闭包修改了这个命名返回值,并且函数直接返回 1 ,但 defer 中的修改使得最终返回 2 。
而return的另一个特性,也会影响return和defer中代码的执行顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package main
import "fmt"
// defer 和 return的详解
func main() {
fmt.Println(foo1())
}
func foo1() int {
defer func() { fmt.Println("This is defer") }()
return func() int {
fmt.Println("This is return")
return 1
}()
}
输出:
This is return
This is defer
1
|
导致上面输出的原因是,return是非原子性的,defer会在return返回值之前执行,但return中的语句,会被全部执行,直到return锚定了某个值或者命名返回值,然后执行defer语句,最后返回return锚定的这个值
|