Golang中defer和闭包的深入解析与应用
Golang中defer和闭包的深入解析与应用 你好
我有以下代码。
https://play.golang.org/p/IkqKQEp27_1
我原本期望它返回一个非 nil 的错误。在 defer 中,我将错误设置为非 nil。但返回的却是 nil。我无法推断出为什么它是 nil。希望能得到一些见解。我肯定是误解了规范。
import (
"fmt"
"log"
)
func cleanUp() error {
return fmt.Errorf("**ERROR: cleanUp() error")
}
func getMessageBug()(string, error) {
var err error
s := "Ok"
fmt.Println(&err, err)
defer func() {
err = cleanUp()
s = "This too is buggy"
fmt.Println(&err, err)
}()
return s, err
}
func main() {
msg, err:= getMessageBug()
if err != nil {
log.Println(err)
}
log.Println(msg)
}
更多关于Golang中defer和闭包的深入解析与应用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
为了使返回值能在延迟函数中被修改,必须为返回值命名:https://play.golang.org/p/MdnuGzMM-UD
func main() {
fmt.Println(f())
}
func f() (result string) {
defer func() {
result = "world"
}()
return "hello"
}
更多关于Golang中defer和闭包的深入解析与应用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个经典的Go语言defer与返回值捕获时机的问题。你的代码中,getMessageBug 函数在 return s, err 时,实际上已经将返回值(包括 err 的当前值,即 nil)保存到了函数的返回值存储区。随后,defer 函数修改的是局部变量 err,而不是已经确定的返回值。
让我们通过一个更清晰的示例来说明:
package main
import (
"fmt"
)
func demonstrate() (result int) {
defer func() {
result = 42 // 直接修改命名返回值
}()
return 0 // 这里会将0赋给result,但defer会在return之后执行,最终返回42
}
func demonstrateBug() (int, error) {
var err error
defer func() {
err = fmt.Errorf("defer error")
}()
return 100, err // 此时err为nil,返回值已经确定。defer修改的是局部变量err,不影响返回值
}
func demonstrateFixed() (result int, err error) {
defer func() {
err = fmt.Errorf("defer error") // 直接修改命名返回值err
}()
result = 100
return // 等价于 return result, err
}
func main() {
fmt.Println(demonstrate()) // 输出: 42
val, err := demonstrateBug()
fmt.Printf("Bug: val=%d, err=%v\n", val, err) // 输出: Bug: val=100, err=<nil>
val, err = demonstrateFixed()
fmt.Printf("Fixed: val=%d, err=%v\n", val, err) // 输出: Fixed: val=100, err=defer error
}
在你的原始代码中,getMessageBug 函数使用了非命名返回值。return s, err 这一行执行时,Go 会将 s 和 err 的当前值复制到函数的返回值中。此时 err 是 nil。随后,defer 函数执行,修改了局部变量 err,但为时已晚,返回值已经确定。
要修复这个问题,使 defer 能够修改返回的错误,你需要使用命名返回值:
func getMessageFixed() (s string, err error) {
s = "Ok"
defer func() {
err = cleanUp()
s = "This is fixed"
}()
return // 等价于 return s, err
}
在这个修复版本中,s 和 err 都是命名返回值。return 语句(没有显式指定返回值)会返回 s 和 err 的当前值。defer 函数直接修改了命名返回值 err 和 s,因此修改会生效。
关键点在于:
- 对于非命名返回值,
return语句中的表达式会在 defer 函数执行前求值并复制到返回值存储区。 - 对于命名返回值,defer 函数可以直接修改它们,因为 defer 执行时,函数虽然已经
return,但还没有返回到调用方,命名返回值变量仍然在作用域内。
这就是为什么你的代码返回了 nil 错误,而使用命名返回值可以解决这个问题的原因。

