延迟和命名的返回值如何在golang中起作用?

Lut*_*ion 31 go

我刚开始学习golang,我得到了一个例子混淆有关使用推迟在golang博客文章更改名为返回值这里.

这个例子说:

  1. 延迟函数可以读取并分配给返回函数的命名返回值.

在此示例中,延迟函数在周围函数返回后递增返回值i.因此,此函数返回2:

func c() (i int) {
    defer func() { i++ }()
    return 1
}
Run Code Online (Sandbox Code Playgroud)

但正如我从A Tour of Go中学到 的 - 命名的回报值

不带参数的return语句返回指定的返回值.这被称为"裸体"回归.

我在下面的代码和函数b中测试它返回1,因为它不是上面提到的没有参数A return语句.

func a() (i int) { // return 2
    i = 2
    return
}

func b() (i int) {  // return 1 
    i = 2
    return 1
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是在第一个例子中,周围的函数c有一个命名的返回值i,但函数c使用return 1,在第二个例子中我们可以看到它应该返回1,无论我是什么值.但是为什么在我更改延迟函数后,c函数返回i的值而不是值1?

当我输入我的问题时,我可能已经猜到了答案.是因为

return 1 
Run Code Online (Sandbox Code Playgroud)

等于

i = 1
return 
Run Code Online (Sandbox Code Playgroud)

在具有命名返回值变量i的函数中?

请帮我确认一下,谢谢!

Roy*_*Lee 36

defer语句将函数调用推送到列表中.在周围函数返回后执行已保存调用的列表.- Go Blog:推迟,恐慌和恢复

另一种理解上述陈述的方法:

延迟语句函数调用推送到堆栈.弹出保存的调用堆栈(LIFO)和延迟函数在周围函数返回之前立即被调用.

 func c() (i int) {
    defer func() { i++ }()
    return 1
}
Run Code Online (Sandbox Code Playgroud)

返回1后,func() { i++ }()执行延迟.因此,按执行顺序:

  1. i = 1(返回1)
  2. i ++(推迟func从堆栈中弹出并执行)
  3. i == 2(命名变量i的最终结果)

为了理解:

 func c() (i int) {
    defer func() { fmt.Println("third") }()
    defer func() { fmt.Println("second") }()
    defer func() { fmt.Println("first") }()

    return 1
}
Run Code Online (Sandbox Code Playgroud)

处决顺序:

  1. i = 1(返回1)
  2. "第一"
  3. "第二"
  4. "第三"

  • _"函数返回后执行者"_不正确.规范非常清楚地表明_"延迟函数在周围函数返回之前立即调用"_,而不是之后.如果在返回后调用deferred,则在`func c`的情况下,它们将无法更改返回值.执行的顺序应该是:`i = 1`(assign)`i ++`(延迟增量)`return`返回2 (4认同)
  • 谢谢,我了解关于延迟的 LIFO 事情,实际上我很难理解命名的返回值。现在我清楚了! (2认同)

Mar*_*ark 8

根据 Go 规范:

返回语句 指定结果的“返回”语句在执行任何延迟函数之前设置结果参数。

Defer 语句 “......在周围函数返回之前立即调用延迟函数......”

所以是的,正如您所假设的,指定的返回变量被赋值,然后延迟语句将它递增。

我想补充一点,命名返回参数可能会导致细微的错误,除非别无选择,否则通常应该避免。


Ahm*_*aan 5

我认为混淆是关于功能的功能如果你这样分类怎么样:

  func main() {
      fmt.Println(c()) //the result is 5
  }

  // the c function returned value is named j
  func c() (j int)  {
      defer changei(&j)
      return 6
  }
  func changei(j *int) {
      //now j is 6 because it was assigned by return statement 
      // and if i change guess what?! i changed the returned value
      *j--;
  }
Run Code Online (Sandbox Code Playgroud)

但如果返回值的名称不是这样的:

  func main() {
      fmt.Println(c()) //the result will become 6
  }

  // the c function returned value is not named at this time
  func c() int  {
      j := 1
      defer changei(&j)
      return 6
  }
  func changei(j *int) {
      //now j = 1
      // and if i change guess what?! it will not effects the returned value
      *j--;
  }
Run Code Online (Sandbox Code Playgroud)

我希望这将清除混乱,这就是我如何快乐Go编码