Почему я получаю 0 и 1 в следующем примере кода Голанга с отсрочкой - PullRequest
8 голосов
/ 02 мая 2019

Вызов defer дает разные результаты для переменных, объявленных двумя разными способами

package main

import (
    "fmt"
)

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

func c1() (i int) {
    defer func() { i++ }()
    return i
}

func c2() (i int) {
    defer func() { i++ }()
    return 2
}

func main() {
    fmt.Println(c(0)) // Prints 0
    fmt.Println(c1()) // Prints 1
    fmt.Println(c2()) // Prints 3 Thank you icza
}

https://play.golang.org/p/gfnnCZ--DkH

1 Ответ

7 голосов
/ 02 мая 2019

В первом примере i является (входящим) параметром. В операторе return возвращаемое значение оценивается, и после этого выполняется отложенная функция, и увеличение значения i на это не влияет на возвращаемое значение.

Во втором примере i - это имя параметра результата. В операторе return вы явно возвращаете значение i, которое затем присваивается возвращаемому значению i (это неоперация). Но отложенным функциям разрешено изменять значения возвращаемых «переменных», и если они это сделают, это повлияет на фактические возвращаемые значения.

Это станет понятнее, если мы добавим еще один пример:

func c2() (i int) {
    defer func() { i++ }()
    return 2
}

Эта функция вернет 3, поскольку оператор return 2 назначит 2 для i, тогда отложенная функция будет увеличивать его, и поэтому возвращаемое значение будет 3. Попробуйте это на Go Playground . Соответствующая часть из Spec: операторы возврата:

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

Как правило, если функция (или метод) имеет именованные параметры результата, возвращаемые значения всегда будут значениями этих переменных, но не следует забывать, что оператор return может назначать новые значения этим параметрам параметра и они могут быть изменены отложенными функциями после a return оператора.

Это упомянуто в Spec: Отложенные заявления :

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

Это также упоминается в блоге Отсрочка, паника и восстановление :

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

А также в Effective Go: Recover :

Если doParse паникует, блок восстановления установит возвращаемое значение на nil - отложенные функции могут изменять именованные возвращаемые значения.

См. Связанный вопрос: Как вернуть значение в функции Go, которая вызывает панику?

...