Что такое функция init() в Go и каковы её особенности?

Ответ

init() — это специальная функция в Go, которая выполняется автоматически при инициализации пакета, до вызова функции main().

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

  • Автоматический вызов: init() вызывается средой выполнения Go, её нельзя вызвать явно.
  • Порядок выполнения:
    1. Сначала init() вызываются для всех импортируемых пакетов в порядке их импорта.
    2. Затем init() вызываются в текущем пакете. Если в одном файле или пакете несколько функций init(), они выполняются в порядке их лексического объявления.
  • Нет аргументов и возвращаемых значений: Функция всегда имеет сигнатуру func init().
  • Множественность: В одном пакете (и даже в одном файле) может быть несколько функций init().

Когда использовать init():

Функция init() полезна для задач, которые нужно выполнить один раз при запуске программы:

  • Инициализация глобальных переменных: Например, установка соединения с базой данных.
  • Регистрация драйверов: Классический пример — регистрация драйвера базы данных: import _ "github.com/lib/pq".
  • Проверка конфигурации: Проверка наличия необходимых переменных окружения или конфигурационных файлов.

Пример:

package main

import (
    "fmt"
    "log"
)

// Глобальная переменная, которая будет инициализирована
var config map[string]string

// Первая init-функция
func init() {
    fmt.Println("Первый init: инициализация конфигурации")
    config = make(map[string]string)
    config["version"] = "1.0"
}

// Вторая init-функция
func init() {
    fmt.Println("Второй init: проверка конфигурации")
    if config["version"] == "" {
        log.Fatal("Версия не установлена!")
    }
}

func main() {
    fmt.Println("main: приложение запущено")
    fmt.Printf("Версия из конфига: %sn", config["version"])
}

Вывод программы:

Первый init: инициализация конфигурации
Второй init: проверка конфигурации
main: приложение запущено
Версия из конфига: 1.0

Когда не стоит использовать init():

Несмотря на удобство, init() следует использовать с осторожностью:

  • Скрывает зависимости: Код в init() выполняется неявно, что затрудняет понимание и тестирование.
  • Усложняет тестирование: Тесты могут падать из-за глобального состояния, созданного в init(). Лучше использовать явные функции инициализации, которые можно контролировать в тестах.
  • Обработка ошибок: init() не может вернуть ошибку. Единственный способ сообщить о сбое — вызвать panic(), что аварийно завершит программу.