在 Go 语言中,return 语句并不是一个原子操作。当 deferreturn 同时出现时,其执行顺序可以拆解为三个步骤。

执行步骤

  1. 返回值赋值:将 return 语句后面的结果值赋给返回值变量(可能是匿名的,也可能是命名的)。
  2. 执行 defer:按照后进先出(LIFO)的顺序执行所有已注册的 defer 函数。
  3. 真正的 RET 指令:携带当前返回值变量中的值退出函数。
核心结论

defer 的执行时机是在 返回值赋值之后函数真正返回之前

场景分析

1. 匿名返回值

如果函数是匿名返回值,defer 中修改变量通常不会影响最终结果,因为 defer 修改的是局部变量,而返回值在第一步已经完成了拷贝。

func anonymousReturn() int {
    var i int
    defer func() {
        i++ // 修改的是局部变量 i
    }()
    return i // 1. 返回值 = i (0); 2. 执行 defer (i=1); 3. 返回 0
}

2. 命名返回值

如果函数是命名返回值,defer 直接操作的就是返回值变量,因此修改会生效。

func namedReturn() (i int) {
    defer func() {
        i++ // 直接修改命名返回值变量 i
    }()
    return i // 1. i = i (0); 2. 执行 defer (i=1); 3. 返回 1
}

3. 指针类型的返回值

无论是匿名还是命名,如果返回的是指针,defer 修改指针指向的内容,最终外部获取到的内容会改变(虽然指针地址没变)。

func pointerReturn() *int {
    i := 0
    defer func() {
        i++
    }()
    return &i // 返回的是 i 的地址,defer 修改了地址对应的值
}

总结

理解 deferreturn 顺序的关键在于:return 语句会先给返回值赋值,然后再执行 defer

  • 匿名返回值return 值的快照已生成,defer 修改原变量不影响快照。
  • 命名返回值return 赋值的对象就是那个命名变量,defer 修改它会直接影响结果。