логотип PurpleSchool
логотип PurpleSchool

Использование defer в Golang

Автор

Олег Марков

Введение

Добро пожаловать в мир Go и одного из его уникальных аспектов - defer. Если вы когда-нибудь задумывались, как эффективно управлять ресурсами и обеспечивать чистый выход из функций в вашем коде на Go, то вы попали по адресу. Defer - это не просто ключевое слово, это ваш помощник в создании устойчивого и поддерживаемого кода. В этой статье мы с вами разберем, что такое defer, как он работает и почему его так важно использовать в развитии программ на Go.

Что такое defer

Начнем с того, что defer - это ключевое слово в Go, которое используется для откладывания выполнения функции до выхода из текущего блока кода. Это особенно полезно для закрытия файлов, разблокировки mutex'ов, освобождения памяти и других операций очистки, которые должны выполняться независимо от того, как происходит выход из функции (в том числе при возникновении ошибок).

Как работает defer

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

package main

import "fmt"

func main() {
    fmt.Println("Начало")

    defer fmt.Println("Это будет выполнено последним")

    fmt.Println("Середина")
}
// Вывод будет таким:
// Начало
// Середина
// Это будет выполнено последним

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

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

package main

import "fmt"

func main() {
    fmt.Println("Начало")

    defer fmt.Println("Первый отложенный вызов")
    defer fmt.Println("Второй отложенный вызов")
    defer fmt.Println("Третий отложенный вызов")

    fmt.Println("Середина")
}
// Вывод:
// Начало
// Середина
// Третий отложенный вызов
// Второй отложенный вызов
// Первый отложенный вызов

Обратите внимание, defer хранит вызовы в стеке и, следовательно, выполняет их в порядке LIFO (last-in, first-out).

Практические примеры

Часто defer используется для работы с файлами. Например, откроем файл и гарантируем, что он будет закрыт после завершения работы с ним.

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Ошибка открытия файла:", err)
        return
    }

    // Отложенное закрытие файла
    defer file.Close()

    // Работайте с файлом здесь
    fmt.Println("Файл открыт успешно")
}

Здесь file.Close() будет выполнен после завершения функции main, что гарантирует, что файл будет обязательно закрыт, даже если в функции произойдет ошибка.

Использование с Recover

Defer также часто используется вместе с recover, чтобы безопасно восстановиться после паники. Это может помочь в создании более стабильного приложения, где можно справляться с неожиданными ошибками.

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Восстановились после паники:", r)
        }
    }()

    fmt.Println("Вызов, который вызовет панику")
    panic("Аварийная остановка программы")
}

В данном примере, функция recover активируется только если происходит паника, позволяя приложению завершиться корректно или продолжить работу в безопасном режиме.

Заключение

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

Стрелочка влевоОбработка ошибок в GoЗначения default в GolangСтрелочка вправо

Все гайды по Golang

Работа с YAML в GolangПреобразование типов в GolangКонвертация структур в JSON в GolangStrconv в GolangИспользование пакета SQLx для работы с базами данных в GolangРазбираемся с SQL в GolangРазделение строк с помощью функции split в GolangSort в GoПоиск и замена строк в Go - GolangИспользование пакета reflect в GolangРабота с PostgreSQL в GoPointers в GolangПарсинг в GoРабота со списками (list) в GolangПреобразование int в string в GolangРабота с числами с плавающей точкой в GolangРабота с полями в GolangИспользование enum в GolangОбработка JSON в GoЧтение и запись CSV-файлов в GolangРабота с cookie в GolangРегистры в GoКэширование данных в GolangПреобразование byte в string в GolangByte в GoИспользование bufio для работы с потоками данных в GolangДобавление данных и элементов (add) в Go
Логирование в Golang. Zap, Logrus, Loki, GrafanaРабота с Docker-контейнерами в GoИспользование pprof в GolangМеханизмы синхронизации в GolangРабота с пакетом S3 в GolangМониторинг Golang приложений с помощью PrometheusОптимизация проектов на GoПаттерны проектирования в GolangМиграции базы данных в GolangОркестрация контейнеров Go с Kubernetes + DockerGjGo Playground и компилятор GolangИспользование go mod init для создания модулей GolangРабота с переменными окружения (env) в GolangКоманда go build в GolangАвтоматизация Golang проектов — CI/CD с GitLab CI и JenkinsОтладка кода в GolangЧтение и использование конфигурации в приложениях на GolangКомпиляция в GolangКак развернуть Go-приложение на облаке AWSАутентификация в Golang
Сетевые протоколы в GoПеременные в GolangЗначения в GolangДженерик %T и его применение в GolangТипы данных в GolangИспользование tls в GolangИспользование tag в структурах GolangSwitch в GoСтроки в GolangРабота с потоками (stream) в GolangSelect в GoРуны в GoРабота с пакетом params в GolangКонвертация строк в числа в GolangNull, Nil, None, 0 в GoНаименования переменных, функций и структур в GoInt в GolangУстановка GolangЧтение и установка HTTP заголовков в GolangMethods в GolangGoLand — IDE для разработки на Golang от JetBrainsОбработка «not found» в GolangFloat в GolangФлаги командной строки в Go (Golang)Запуск внешних команд в GolangОбработка ошибок в GoИспользование defer в GolangЗначения default в GolangГенерация кода в GoФорматирование кода в GolangЧистая архитектура в GolangКаналы (channels) в GolangПолучение body из HTTP запроса в Golang
Открыть базу знаний