Как в отложенном вызове (defer) получить доступ к возвращаемым значениям функции?

Ответ

Чтобы получить доступ к результатам выполнения функции внутри defer, необходимо использовать именованные возвращаемые значения в сочетании с анонимной функцией (замыканием).

Важно помнить ключевое правило defer: аргументы для отложенного вызова вычисляются в момент объявления defer, но сам вызов функции происходит при выходе из внешней функции.

Способ 1: Именованные возвращаемые значения (предпочтительный)

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

func example() (result int) { // 'result' — именованное возвращаемое значение
    // Анонимная функция в defer захватывает 'result' из своей области видимости.
    defer func() {
        // Этот код выполнится перед выходом из функции,
        // когда 'result' уже будет присвоено значение 42.
        fmt.Printf("Deferred call. Result is: %dn", result)
        // Здесь можно даже изменить возвращаемое значение!
        result++
    }()

    fmt.Println("Assigning result...")
    return 42
}

// Вызов `example()` вернет 43, а не 42.

Способ 2: Использование указателя

Если вы не хотите использовать именованные возвращаемые значения, вы можете передать в defer указатель на переменную с результатом.

func anotherExample() int {
    var result int
    // Передаем указатель на 'result' в отложенную функцию
    defer func(res *int) {
        fmt.Printf("Deferred call. Result is: %dn", *res)
    }(&result)

    result = 100
    return result
}

Распространенная ошибка

Попытка передать само значение, а не использовать замыкание или указатель, приведет к тому, что в defer будет передано значение переменной на момент объявления defer.

func wrongExample() int {
    var result int
    // ОШИБКА: fmt.Println получит значение 'result' (равное 0) в ЭТОТ момент.
    defer fmt.Println("Result:", result)

    result = 42 // Это изменение не повлияет на значение, переданное в defer
    return result
}
// Выведет: "Result: 0"