ПрограммированиеGo разработчик

Как работает package initialization в Go и какие неожиданные эффекты встречаются при неправильной организации init-функций?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Когда приложение Go запускается, все non-test-пакеты инициализируются строго по определённому порядку: сначала инициализируются все импортируемые зависимости (по цепочке), затем непосредственно сам пакет. Для инициализации используются:

  • package-level переменные (инициализация сверху вниз по файлу)
  • init() функции (может быть несколько на пакет)

Функции init вызываются после инициализации переменных пакета. Если пакет импортируется — его init выполняется один раз до использования переменных/функций этого пакета.

Пример:

var a = getA() func getA() int { fmt.Println("getA called") return 42 } func init() { fmt.Println("init called") } // Выведет: // getA called // init called

Особенности:

  • Порядок инициализации может стать критичен, если переменные одного пакета зависят от переменных другого импортируемого пакета.
  • Несколько init() в одном файле или пакете выполняются в порядке их появления в исходнике.
  • Пакет main инициализируется последним.

Вопрос с подвохом

Можно ли явно вызвать init-функцию любого пакета?

Ответ: Нет, функция init никогда не вызывается напрямую из кода программы. Она вызывается только рантаймом во время процесса инициализации пакета в заранее определённый момент, вызов init() явно не допускается — приведёт к ошибке компиляции.

Примеры реальных ошибок из-за незнания тонкостей


История

В проекте два пакета импортировали друг друга для инициализации глобальных переменных через init(). Это привело к циклическим зависимостям и ошибкам при сборке — Go не смог определить корректный порядок инициализации.


История

Причиной сложного бага стало то, что init-функции использовали глобальные переменные пакета, которые ещё не были проинициализированы. Результат: неоднозначное поведение при запуске с непредсказуемыми состояниями.


История

В проекте для side effects импортировали пакет без явного использования (через import _ "some/lib"). После реорганизации кода init-функция нужного пакета вызывалась слишком рано (до инициализации зависимых переменных в другом пакете), возник hard-to-debug баг.