func f() (r int) { defer func() { r++ }(); return 0 }?Это классический вопрос на понимание того, как defer взаимодействует с именованными возвращаемыми значениями. Ответ: функция вернет 1.
Оператор return в Go - это не атомарная операция. Он состоит из трех шагов:
func f() (r int) {
defer func() { r++ }()
return 0
}
// Шаг 1: r = 0 (return 0 присваивает r = 0)
// Шаг 2: r++ (defer увеличивает r до 1)
// Шаг 3: return r (возвращается 1)
Deferred функция - это замыкание, которое захватывает переменную r по ссылке. Когда замыкание выполняет r++, оно модифицирует ту же переменную, которую функция собирается вернуть.
// С именованным возвращаемым значением:
func withNamed() (r int) {
defer func() { r = 100 }()
return 0
}
// Вернет 100 - defer перезаписал r
// Без именованного возвращаемого значения:
func withoutNamed() int {
r := 0
defer func() { r = 100 }()
return r
}
// Вернет 0 - defer изменил локальную переменную,
// но возвращаемое значение уже зафиксировано
Этот паттерн часто используется для обработки ошибок:
func doWork() (err error) {
f, err := os.Open("file.txt")
if err != nil {
return err
}
defer func() {
closeErr := f.Close()
if err == nil {
err = closeErr // перезаписываем err через defer
}
}()
// ... работа с файлом ...
return nil
}
Здесь defer может установить ошибку закрытия файла как возвращаемое значение, если основная работа прошла успешно (err == nil).