【问题标题】:When defer func evaluates its parameters当 defer func 评估其参数时
【发布时间】:2017-03-09 19:18:51
【问题描述】:

我正在学习 defer 在 golang 中的行为方式,并希望在函数返回时使用它来处理错误。

代码如下:

package main

import "fmt"
import "errors"

func main() {
    a()
}

func a() {
    var err error   
    defer func(){
        if err != nil {
            fmt.Printf("1st defer: %s\n", err)
        } else {
            fmt.Println("1st defer: defer not error")
        }
    }()
    defer func(err error){
        if err != nil {
            fmt.Printf("2nd defer: %s\n", err)
        } else {
            fmt.Println("2nd defer: defer not error")
        }
    }(err)

    err = errors.New("new error")
    if err != nil {
        return
    }
}

输出:

2nd defer: defer not error
1st defer: new error

Doc 表示在评估 defer 调用时评估参数,这似乎应该是一致的。为什么 2 defer 对变量 err 有不同的值,因此输出不同?我知道它与第二个函数有关,将err 作为输入参数,但不知道为什么。

【问题讨论】:

    标签: go error-handling deferred


    【解决方案1】:

    好的,我刚刚想通了。如果您将任何参数传递给 defer 函数(如上面的第二个 defer 函数),这些参数会在 defer 函数被延迟时计算,而不是在执行时计算。这意味着在我的示例中 err 仍然是 nil 并且尚未分配给新错误。

    另一方面,在上面的第一个defer中,err不是参数,而是函数a中的一个变量,当第一个defer执行时,它已经被赋值给了一个新的错误。

    【讨论】:

      【解决方案2】:

      另一种方法是使用对原始 err 变量的引用

      package main
      
      import (
          "errors"
          "fmt"
      )
      
      func main() {
          a()
      }
      
      func a() {
          var err error
          defer func() {
              if err != nil {
                  fmt.Printf("1st defer: %s\n", err)
              } else {
                  fmt.Println("1st defer: defer not error")
              }
          }()
          defer func(err *error) {
              if *err != nil {
                  fmt.Printf("2nd defer: %s\n", *err)
              } else {
                  fmt.Println("2nd defer: defer not error")
              }
          }(&err)
      
          err = errors.New("new error")
          if err != nil {
              return
          }
      }
      

      输出是:

      2nd defer: new error
      1st defer: new error
      

      【讨论】:

      • 这非常有帮助。我无法让我的数据库事务回滚函数工作,因为它在延迟函数中传递了一个错误,但是一旦我开始将错误指针传递给它,它就像一个魅力。
      【解决方案3】:

      Defer StatementDefer Function 的情况下还有另一种类似的情况。请看下面的例子

      package main
      
      import (
          "fmt"
          "time"
      )
      
      func main() {
      
          start := time.Now()
          time.Sleep(3*time.Second)
          defer func() { fmt.Println("Defer Function Elapsed Time: ", time.Since(start)) }() //Defer Function
          defer fmt.Println("Defer Statement Elapsed Time: ", time.Since(start)) //Defer Statement
          time.Sleep(3*time.Second)
      }
      

      输出:

      延迟语句经过时间:3s

      延迟函数经过时间:6s

      go play上面试试

      这是因为在 Deferred Statement 的情况下,延迟调用的参数会被立即评估 refer doc

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多