Внутри пакета переменные уровня пакета инициализируются в порядке объявления, но после любой из переменных, от которых они зависят.
Точнее, переменная уровня пакета считается готовой к инициализации, если она еще не инициализирована и не имеет выражения инициализации или ее выражение инициализации не зависит от неинициализированных переменных. Инициализация продолжается путем многократной инициализации следующей переменной уровня пакета, которая является самой ранней в порядке объявления и готова к инициализации, пока не будет готовых переменных для инициализации.
Если какие-либо переменные все еще неинициализированы после завершения этого процесса, эти переменные являются частью одного или нескольких циклов инициализации, и программа недействительна.
Порядок объявления переменных, объявленных в нескольких файлах, определяется порядком, в котором файлы представляются компилятору: переменные, объявленные в первом файле, объявляются перед любой из переменных, объявленных во втором файле, и так далее.
Анализ зависимостей не основывается на фактических значениях переменных, только на лексических ссылках на них в источнике, анализируемых транзитивно. Например, если выражение инициализации переменной x относится к функции, тело которой относится к переменной y, то x зависит от y. В частности:
- Ссылка на переменную или функцию - это идентификатор, обозначающий эту переменную или функцию.
- Ссылка на метод m - это значение метода или выражение метода в форме t.m, где (статический) тип t не является типом интерфейса, а метод m находится в наборе методов t. Неважно, вызывается ли результирующее значение функции t.m.
- Переменная, функция или метод x зависят от переменной y, если выражение или тело инициализации x (для функций и методов) содержит ссылку на y или на функцию или метод, который зависит от y.
Анализ зависимостей выполняется для каждого пакета; рассматриваются только ссылки на переменные, функции и методы, объявленные в текущем пакете.
Например, с учетом объявлений
var (
a = c + b
b = f()
c = f()
d = 3
)
func f() int {
d++
return d
}
порядок инициализации d, b, c, a.
Переменные также могут быть инициализированы с использованием функций с именем init, объявленных в блоке пакета, без аргументов и без параметров результата.
func init() { … }
Несколько таких функций могут быть определены для пакета, даже в пределах одного исходного файла. В блоке пакета идентификатор init можно использовать только для объявления функций init, но сам идентификатор не объявлен. Таким образом, на функции init нельзя ссылаться из любой точки программы.
Пакет без импорта инициализируется путем присвоения начальных значений всем его переменным уровня пакета с последующим вызовом всех функций init в порядке их появления в источнике, возможно, в нескольких файлах, как представлено компилятору. Если пакет имеет импорт, импортированные пакеты инициализируются перед инициализацией самого пакета. Если несколько пакетов импортируют пакет, импортированный пакет будет инициализирован только один раз. Импорт пакетов по своей конструкции гарантирует отсутствие циклических инициализационных зависимостей.
Инициализация пакета - инициализация переменной и вызов функций init - происходит в одной последовательности, последовательно, по одному пакету за раз. Функция init может запускать другие программы, которые могут выполняться одновременно с кодом инициализации. Однако инициализация всегда выполняет последовательность функций init: она не будет вызывать следующую, пока не вернется предыдущая.
Чтобы обеспечить воспроизводимое поведение при инициализации, системам сборки рекомендуется представлять компилятору несколько файлов, принадлежащих одному и тому же пакету, в лексическом порядке имен файлов.
Читайте также:
- Спецификация Go: пакеты
- Спецификация Go: инициализация и выполнение программы, нулевое значение
- Эффективный Go: инициализация
Комментариев нет:
Отправить комментарий