因为golang没有try……catch的用法,但是可以通过defer + recover来实现,但是要先明确defer与return之间的执行顺序。
defer
先看defer的定义(参考tour go)。
defer关键字可以推迟它所修饰的语句的执行,知道被它包围的代码执行完成后,才执行,一般用于执行一些清理工作(资源释放、关闭连接等等)来简化代码。可以看下面一段代码,代码打开两个文件并把其中一个文件的内容拷贝到另一个文件中,
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
这段代码可以奏效,但是存在bug。如果调用os.Create失败了,函数还没有释放资源就返回了。如果函数更复杂,潜在的bug就会更多了。通过defer我们可以确保文件资源都被释放。
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
defer语句简单有效,简单来看,就三个规则:
1:defer修饰函数的变量在defer被声明时就确定了,例如下面的代码
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
代码最终会输出 0 而不是 1 。这是因为在声明defer修饰的 fmt.Println(i)时,变量i的值就确定了,这时i的值还是 0 。
2:多个defer的定义与执行类似于栈的操作:先进后出,最先定义的最后执行。
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
这段代码的执行结果:
counting
done
9
8
7
6
5
4
3
2
1
0
3:defer修饰的函数可以读取并且赋值给函数的返回值(这里有点翻译的不通顺,先看代码吧)
package main
import "fmt"
func main() {
fmt.Println(c())
}
func c() (i int) {
defer func() { i++ }()
return 1
}
代码最终输出结果是 2 。defer修饰的函数可以读取返回的 i 值并且给他赋新值。
Panic
Panic是一个内置函数,可以打断正常流程。例如一个函数F调用了panic,F停止执行,F所有的defer函数会正常执行,defer函数都执行完后,F函数返回给它的调用者。对于F函数的调用者来说,此时,F函数的返回相当于又调用了一次panic。F函数的调用者就像F函数一样执行相同的逻辑,直到这个goroutine栈里所有的函数都panic返回了,进而整个程序终止了(如果goroutine没有对panic通过defer进行捕捉)。
Panic可以调用panic函数直接触发,也会因为一些其他runtime error触发,例如数组越界等等。
Recover
Recover也是一个内置函数,可以重新获取一个panic的goroutine的控制权。Recover只有和defer结合起来,内置在defer函数中,才会起作用。如果函数正常执行,调用recover()返回的是nil,如果当前goroutine处于panic,调用recover()就可以捕捉到panic时设定的value,然后继续正常的执行流程。
下面是panic和defer机制的一个示例代码,
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
代码输出:
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
未完待续……