目录
1、资源释放
2、异常捕获
3、参数的预计算
4、defer 返回值的陷阱
下面是一个简单的读取文件的程序,os.Open 打开文件资源描述符,在读取文件后,需要释放资源。但是在错误的时候,程序就直接返回那么,资源就得不到释放
func ReadFile(Filename string) ([]byte, error) {
file, err := os.Open(Filename)
if err != nil {
return nil, err
}
stat, err := file.Stat()
if err != nil {
return nil, err
}
var re = make([]byte, stat.Size())
_, err = file.Read(re)
if err!=nil{
return nil,err
}
file.Close()
if err != nil {
return nil, err
}
return re, nil
}
那么程序需要就需要修改为,在所有err 的地方都需要释放资源
file, err := os.Open(Filename)
if err != nil {
file.Close()
return nil, err
}
stat, err := file.Stat()
if err != nil {
file.Close()
return nil, err
}
var re = make([]byte, stat.Size())
_, err = file.Read(re)
if err != nil {
file.Close()
return nil, err
}
但是这样处理很不优雅,而且很容易漏掉,那么我们就可以利用defer 的延迟调用,程序结束的时候释放资源,能减少大量冗余代码避免由于忘记释放资源而产生的错误
func ReadFile(Filename string) ([]byte, error) {
file, err := os.Open(Filename)
defer file.Close()
if err != nil {
return nil, err
}
stat, err := file.Stat()
if err != nil {
return nil, err
}
var re = make([]byte, stat.Size())
_, err = file.Read(re)
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return re, nil
}
避免程序因为panic 异常退出,可以通过defer函数中使用recover进行异常捕获,程序就不会异常退出,main的 fmt.Println 可以打印
func ReadFile(Filename string) ([]byte, error) {
file, err := os.Open(Filename)
defer file.Close()
if err != nil {
return nil, err
}
stat, err := file.Stat()
if err != nil {
return nil, err
}
var re = make([]byte, stat.Size())
_, err = file.Read(re)
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
return re, nil
}
传递到defer 中的函数参数是预执行的,因此在执行 defer 语句时,执行了a+1并将其保留下来,只到函数执行完后才执行 defer 函数体内的语句。
func main() {
a := 1
defer func(b int) {
fmt.Println(b)
}(a + 1)
a = 100
}
有返回值时时先执行defer 还是先执行 return ,具体情况,具体分析,先看几个例子:
var g = 100
func gf() (r int) {
defer func() {
g = 200
}()
fmt.Printf("g:%d\n", g)
return g
}
func main() {
i := gf()
fmt.Println(i)
fmt.Println(g)
}
g:100
100
200
从返回结果看好像是先执行了return 在执行了 defer
那么下面的程序:
var g = 100
func gf() (r int) {
r = g
defer func() {
r = 200
}()
r = 0
return r
}
func main() {
i := gf()
fmt.Println(i)
fmt.Println(g)
}
返回结构:
200
100
从返回结果好像是先执行了defer 后执行了 return
那么为什么会这样呢,原因是return 不是一个原子操作,包含了下面几步:
将返回值保存在栈上->执行defer 函数->函数返回
对于第一个例子:
g=100
r=g
g=200
return
对于第二个例子
g=100
r=g
g=200
return