Что такое `defer` в Go и каковы его ключевые особенности?

Ответ

Ключевое слово defer в Go используется для отложенного выполнения вызова функции до тех пор, пока содержащая её функция не завершит свое выполнение. Основное предназначение defer — это управление ресурсами, такими как закрытие файлов, разблокировка мьютексов или закрытие сетевых соединений.

Пример с закрытием файла:

func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    // file.Close() будет вызван непосредственно перед выходом из processFile,
    // независимо от того, как функция завершится (через return или панику).
    defer file.Close()

    // ... код для работы с файлом ...
    return nil
}

Ключевые особенности defer:

  1. Порядок выполнения LIFO (Last-In, First-Out): Если в функции есть несколько defer вызовов, они будут выполнены в порядке, обратном их объявлению. Последний объявленный defer выполнится первым.

    func lifoExample() {
        defer fmt.Println("третий")
        defer fmt.Println("второй")
        defer fmt.Println("первый")
    } 
    // Вывод будет:
    // первый
    // второй
    // третий
  2. Вычисление аргументов в момент вызова: Аргументы отложенной функции вычисляются в тот момент, когда компилятор встречает оператор defer, а не когда функция фактически выполняется.

  3. Гарантированное выполнение: Отложенный вызов гарантированно выполнится, даже если в функции произойдет panic. Это делает defer идеальным инструментом для очистки ресурсов.

  4. Взаимодействие с именованными результатами: defer может читать и изменять именованные возвращаемые значения функции. Это полезно для обработки ошибок или модификации результата перед возвратом.

    func getNumber() (result int) {
        defer func() {
            // Этот код выполнится после 'return 10',
            // но до фактического возврата значения из функции.
            result *= 2
        }()
        return 10
    }
    // getNumber() вернет 20