GO成神之路:defer中的坑|Go主题月

289 阅读2分钟

匿名返回值与剧名返回值的坑

深入defer(续)中我们已经列举了匿名返回值与具名返回值产生的疑惑,并在defer与return到底谁先执行?中通过汇编代码的角度进行了解答。

package main

import "log"

func main() {
	log.Println(testA())
	log.Println(testB())
}

func  testA() int {
	a:=1
	defer func() {
		a=2
	}()
	return a
}

func testB() (a int) {
	a=1
	defer func() {
		a=2
	}()
	return a
}

# out
1
2

避坑:避免在开发中使用这种让人产生疑惑的用法

闭包的坑

package main

import "log"

func main(){
	for i:=1;i<5;i++{
		defer func() {
			log.Println(i)
		}()
	}
}
# out
5
5
5
5

正确的写法

package main

import "log"

func main(){
	for i:=1;i<5;i++{
		defer func(a int) {
			log.Println(a)
		}(i)
	}
}

# out
4
3
2
1

开发时不要再循环中使用defer,defer定义的函数需要压栈,还要为存储defer申请额外的内存,所以循环中能不适用defer的,尽量不用

使用函数做参数的坑

package main

import "log"

func main(){
	defer log.Println(test())
	log.Println(2)
}

func test() int {
        log.Println(3)
	return 1
}

# out
3
2
1

defer定义的函数如果包含函数参数,defer在压栈之前会将函数参数先执行,将执行结果随defer定义的函数一起压栈

os.Exit导致defer不会被调用

os.Exit函数并不常用,但是应该知道,它会出现的位置直接退出

package main

import (
	"log"
	"os"
)

func main(){
	defer func() {
		log.Println(1)
	}()
	os.Exit(1)
}

# out
什么都没有输出

避坑:确保你了解os.Exit的作用,并能在合适的时候使用它,尽量使用return结束方法

总结

  1. 不要在defer函数中使用defer函数之外定义的变量,如果要使用尽量传递进去
  2. 不要再循环语句中使用defer函数
  3. 不要再defer函数中使用函数作为参数

每种语言都有自己的不好的地方,在使用过程中,要知道问题所在,但努力避免使用,不要明知有坑还不填坑