今天遇到了一个问题,我需要设置一个循环中的变量用于下一次循环之中。因为修改的时候一些问题(处理err),所以不小心将之前的=改为了:=。大致如下


func getEle() (int,error){
	return rand.Intn(100),nil
}


func exchangeGradually(fp int){
	var fingerprint int = fp
	for i:=0;i<5;i++{
		oldfingerprint := fingerprint
		fingerprint,err := getEle()
		if err != nil{
			panic(err)
		}
		fmt.Println(fingerprint,oldfingerprint)

	}
}

func main() {
	exchangeGradually(10)
}

可以试着执行一下,原本预期中oldfingerprint是随着fingerprint改变而改变的,但是实际上是不变的。原因是很简单的,因为:=重新分配了一个变量覆盖掉了原有的变量。

但是我原本以为是不会覆盖的,因为之前写错误处理的时候往往也是直接val,err := ...这样写下来。现在看来,:=应该是会重新声明左侧的所有变量并覆盖作用域。

从这里可以引出shadow error的问题,类似于shadow variable。shadow error是指很多时候需要在defer中处理error,但是被后面的错误给覆盖了,类似于

func getErr1() (int,error){
	return 1,fmt.Errorf("error 1")
}

func getErr2() (int,error){
	return 2,fmt.Errorf("error 2")
}


func exchangeGradually(fp int){
	a1,err := getErr1()
	fmt.Println(a1,err)
	defer func() {
		fmt.Println(err)
	}()
	a2,err := getErr2()
	fmt.Println(a2,err)
}

func main() {
	exchangeGradually(10)
}

在下面这个例子中,原本预期要处理的是error1,但是实际输出的却是error2。因为defer针对的err是函数作用域的,该变量被后续的新声明给覆盖了(当然,实际上原理是不一样的,这个主要是defer中传值与传引用的问题,只要加上捕获列表即可)。

func exchangeGradually(fp int){
	a1,err := getErr1()
	fmt.Println(a1,err)
	defer func(err error) {
		fmt.Println(err)
	}(err)
	a2,err := getErr2()
	fmt.Println(a2,err)
}