Короткое объявление переменной: когда оно создаёт новое имя, а когда переиспользует существующее?
Оператор := создаёт переменные в текущем лексическом блоке, но может переиспользовать уже объявленные в этом же блоке имена. При этом справа от := должна появиться хотя бы одна новая переменная; одно только переобъявление приводит к ошибке компиляции.
Имя из внешнего блока не переиспользуется: такое объявление создаёт новую переменную и затеняет внешнюю.
Короткое объявление появилось как часть минималистичного синтаксиса Go для локальных переменных. Оно сокращает распространённый шаблон объявления переменной с последующим присваиванием, особенно при обработке нескольких возвращаемых значений.
Одновременно правило привязано к лексической области видимости. Это позволяет безопасно писать распространённые конструкции вроде одновременного получения результата и ошибки, не превращая любое совпадение имени в неявное изменение переменной из внешнего блока.
Основной риск связан с ошибочным предположением, что одинаковое имя всегда означает одну и ту же переменную. Вложенный блок может создать переменную с тем же именем, поэтому присваивание будет менять внутреннее значение, а внешнее останется прежним.
Обратная проблема возникает при повторном использовании := в одном блоке: если новые имена отсутствуют, программа не компилируется. Отдельно учитывается идентификатор _: он отбрасывает значение и не считается новой переменной.
В коротком объявлении Go рассматривает каждое имя слева отдельно. Уже объявленное имя можно переиспользовать только при одновременном выполнении трёх условий:
Если имя найдено только во внешнем блоке, оно не переиспользуется. Вместо этого создаётся локальная переменная внутреннего блока — это затенение.
В строке y, err := ... переменная err переиспользуется, потому что уже существует в том же блоке, а y создаётся впервые. Вложенное x := 3 создаёт отдельное имя; после завершения внутреннего блока снова доступно внешнее x.
Практическое следствие: если требуется изменить существующую переменную без создания новой, используется обычное присваивание =. Если намеренно нужно создать локальную переменную с тем же именем во вложенном блоке, := допустим, но такое затенение следует применять осторожно.
В обработчике запроса уже существует переменная err, а затем разработчик добавляет вложенную проверку и пишет короткое объявление с тем же именем. Ошибка может быть сохранена во внутренней переменной, тогда как внешний err останется без изменения; последующая проверка внешней переменной даст неверный результат.
Варианты решения:
= — плюс: явно изменяется существующая переменная; минус: все имена слева должны быть уже объявлены;:= во вложенном блоке — плюс: локальная область не конфликтует с внешней; минус: легко случайно затенить важное имя;Для обработчиков ошибок обычно выбирают = при необходимости обновить состояние внешнего блока или осмысленное новое имя при независимой локальной проверке. Это устраняет скрытое затенение и делает область действия ошибки очевидной при ревью.
Что произойдёт, если справа от := нет новых имён?
Компилятор выдаст ошибку вроде no new variables on left side of :=. Повторное объявление всех имён в том же блоке не разрешено, даже если правые выражения имеют подходящие типы. Идентификатор _ не исправляет ситуацию, потому что он не создаёт переменную.
Может ли := переиспользовать имя из внешнего блока?
Нет. Правило переиспользования действует только для переменной, объявленной в том же блоке. Во вложенном блоке одинаковое имя создаёт новую переменную и затеняет внешнюю; изменения внутренней переменной не меняют внешнюю.
Почему в конструкции с результатом и ошибкой часто используют := повторно?
Например, после первого вызова в блоке уже есть err, но следующий вызов возвращает новый результат и ошибку. Запись с := разрешена, если хотя бы результат является новым именем: ошибка переиспользуется, а результат объявляется. Это позволяет компактно обрабатывать несколько вызовов, но при переходе во вложенный блок та же запись уже может начать затенять внешнюю ошибку.