Как работает отсрочка и имя возвращаемого значения в golang?

Я только начал изучать golang, и я запутался в одном примере об использовании defer для изменения имени возвращаемого значения в сообщении блога golang здесь

В примере говорится

  1. Отложенные функции могут читать и присваивать возвращаемой функции с именами возвращаемых значений.

В этом примере отложенная функция увеличивает возвращаемое значение я после возвращения функции окружения. Таким образом, эта функция возвращает 2:

func c() (i int) {
    defer func() { i++ }()
    return 1
}

Но как то, что я узнал frmm A Tour of Go - Именованные возвращаемые значения

Возвращаемый оператор без аргументов возвращает именованные возвращаемые значения. Это называется "голым" возвратом.

Я тестировал в следующем коде и в функции b он возвращает 1, потому что это не был оператор возврата без аргументов, упомянутый выше.

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

func b() (i int) {  // return 1 
    i = 2
    return 1
}

Итак, мой вопрос находится в первом примере, окружающая функция c имеет именованное возвращаемое значение i, но функция c использует return 1, которая во втором примере может видеть, что она должна иметь return 1 независимо от того, какое значение я есть. Но почему после я изменений в отложенной функции функция c возвращает значение я вместо значения 1?

Когда я вводил свой вопрос, я, возможно, догадался об ответе. Это потому, что

return 1 

равно

i = 1
return 

в функции с именованной переменной возвращаемого значения i?

Пожалуйста, помогите мне подтвердить, спасибо!

Ответ 1

Оператор defer вызывает вызов функции в список. Список сохраненных вызовов выполняется после возвращения внешней функции. - Блог Go: Отложить, Паника и Восстановление

Еще один способ понять приведенное выше утверждение:

Операторы defer выталкивают вызов функции в стек . Стек сохраненных вызовов выскочил (LIFO) и выполнил после возврата функции.

 func c() (i int) {
    defer func() { i++ }()
    return 1
}

После возврата 1 отсрочка func() { i++ }() выполняется. Следовательно, в порядке выполнения:

  • я = 1 (возврат 1)
  • я ++ (defer func выскочит из стека и выполнил)
  • я == 2 (конечный результат именованной переменной i)

Для понимания:

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

    return 1
}

Порядок выполнения:

  • я = 1 (возврат 1)
  • "первый"
  • "второй"
  • "третий"

Ответ 2

Согласно спецификации Go:

Отчеты о возврате Оператор "return", который задает результаты, устанавливает параметры результата перед выполнением любых отложенных функций.

Отложить заявления "... отложенные функции вызывают непосредственно перед возвратом окружающей функции..."

Итак, как вы предполагали, назначается именованная возвращаемая переменная, тогда оператор отложенной установки увеличивает его.

Я бы добавил, что именованные возвращаемые параметры могут приводить к тонким ошибкам и, как правило, следует избегать, если нет альтернативы.

Ответ 3

Я думаю, что путаница связана с функцией в функции, если вы классифицируете ее следующим образом:

  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--;
  }

но если возвращаемое значение не названо так:

  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--;
  }

Я надеюсь, что это очистит путаницу, и именно так я сделал счастливое кодирование Go