面试题答案
一键面试Go版本差异及原因分析
- Go 1.10及之前:在Go 1.10及之前版本,defer语句在函数返回值确定之后执行,但在实际返回之前。这意味着defer修改返回值是有效的。例如:
package main
import "fmt"
func oldVersion() (int, string) {
var a int = 1
var s string = "original"
defer func() {
a = 2
s = "modified"
}()
return a, s
}
在这个例子中,调用oldVersion()
返回的是2
和"modified"
。原因是defer语句在返回值确定后执行,它能修改已经确定的返回值。
- Go 1.13及之后:从Go 1.13开始,defer语句在函数返回值确定之前执行。这一改变是为了遵循更清晰的求值顺序规则。例如同样的代码:
package main
import "fmt"
func newVersion() (int, string) {
var a int = 1
var s string = "original"
defer func() {
a = 2
s = "modified"
}()
return a, s
}
在Go 1.13及之后版本,调用newVersion()
返回的是1
和"original"
。因为defer语句在返回值确定之前执行,return
语句中的a
和s
值已经在defer执行之前就确定了,defer对它们的修改无效。
避免版本差异陷阱的方法
- 使用命名返回值并在defer中直接操作返回值:
package main
import "fmt"
func safeVersion() (result int, str string) {
result = 1
str = "original"
defer func() {
result = 2
str = "modified"
}()
return
}
在这个例子中,无论在哪个Go版本,调用safeVersion()
都会返回2
和"modified"
。因为命名返回值在defer执行时已经绑定到了返回值变量,defer对它们的修改是直接作用于返回值的。
- 避免在defer中修改返回值:如果不需要在defer中修改返回值,那么不同版本之间就不会存在这种差异。例如:
package main
import "fmt"
func anotherSafeVersion() (int, string) {
var a int = 1
var s string = "original"
defer func() {
fmt.Println("This defer doesn't modify return values")
}()
return a, s
}
这样的代码在不同Go版本下行为一致。通过合理设计defer语句的逻辑,避免在defer中修改返回值,可以有效防止因版本差异导致的问题。