关于不同的命名返回值与正常返回值的问题
我的代码
package main
import "fmt"
func main() {
f := fmt.Println
f(a())
f(b())
}
func a() int {
i := 0
defer func() {
i += 1
fmt.Println("a defer : ", i)
}()
return i
}
func b() (i int) {
i = 0
defer func() {
i += 1
fmt.Println("b defer : ", i)
}()
return i
}
结果:
a函数返回0
b函数reutrn 1
为什么?
答案 0 :(得分:5)
命名返回值还为函数范围分配变量。
func a() int
:虽然您已经返回i = 0
的值,但由于未定义任何命名值,因此返回了静态值。因此,即使您在延迟函数中增加i
,它也不会影响返回的值。
func b() (i int)
:已分配变量i
(已初始化为0
)。即使延迟函数在返回i = 0
之后运行,范围仍然可访问,因此仍然可以更改。
另一种观点:您仍然可以在延迟函数中更改命名返回值,但不能更改常规返回值。
在以下示例中尤其如此:
func c() (i int) {
defer func() {
i = 1
fmt.Println("c defer : ", i)
}()
return 0
}
答案 1 :(得分:1)
defer
在return
语句之后但在函数返回之前运行一个函数,从而启用修改返回的结果。但是,只能通过变量名称正常访问命名的返回结果。
return
语句,当不是赤裸裸的时候(关于命名返回的另一件事,但在这里不相关),表达式得到了评估。如果命名了返回,则为命名变量分配评估值。
在您的代码中,在func a() int
中输入的内容但未命名。因此,当return i
被执行时,它会将返回值(代码不可用的变量)设置为i
的值。您可以将其视为RETVAL := i
。之后,您的延迟函数已修改i
,但返回值(RETVAL
)保持不变。
但是在func b() (i int)
中,我是一个命名的回归。因此,当return i
执行时,它会逐字转换为i = i
。之后,您的deffered函数修改了i
,返回值,因此返回的值会更改。
有关return
的更多信息:https://golang.org/ref/spec#Return_statements
答案 2 :(得分:0)
使用命名的返回值,您可以直接修改返回的内容,使用“normal”返回值只需修改函数范围内的局部变量,该变量永远不会被返回。
更多相关内容:
延迟函数可以访问命名的返回值,但它本身没有返回值 - 所以这实际上是从那里修改主函数结果的唯一方法。非常有用的东西。
想象一下,您想要修复恐慌的代码 - 您希望它返回错误。您可以在延迟函数中使用recover
然后将已恢复的错误分配给命名的返回值来解决此问题。
示例,有点抽象,但希望有用:
func noMorePanics() (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
potentiallyPanickingFunction()
}