К чему приведёт вызов метода через интерфейсную переменную, оставленную в нулевом состоянии, и почему?
Вызов метода через nil-интерфейс завершится паникой во время выполнения. У такого интерфейсного значения отсутствуют и динамический тип, и динамическое значение, поэтому Go не может определить реализацию метода для вызова.
Интерфейсы Go предназначены для отделения кода, использующего поведение, от конкретной реализации. Это позволяет передавать разные типы через общий набор методов и выбирать реализацию динамически.
Нулевое значение интерфейса сделано равным nil, чтобы отсутствие реализации можно было явно представить без создания специального объекта-заглушки. Обратная сторона — попытка вызвать метод до присваивания конкретного значения приводит к ошибке во время выполнения.
Интерфейсная переменная может быть объявлена, но не инициализирована. Такая переменная не содержит информации о том, какой конкретный тип должен обработать вызов метода.
Если не проверить интерфейс перед вызовом, программа может завершиться паникой в рабочем сценарии: например, при необязательной зависимости, неуспешной инициализации или возврате отсутствующего обработчика.
Интерфейсное значение концептуально состоит из пары: динамический тип и динамическое значение. У nil-интерфейса обе части отсутствуют. Поэтому вызов метода невозможен: сначала требуется найти метод в динамическом типе, но этого типа нет.
Это отличается от интерфейса, содержащего указатель nil. В таком случае динамический тип присутствует, поэтому диспетчеризация метода возможна. Дальнейшее поведение зависит от реализации метода: он может корректно обработать nil-указатель или сам вызвать панику.
Безопасный код должен заранее определить контракт. Возможные варианты — проверить интерфейс на nil, гарантировать создание реализации при конструировании объекта или использовать объект-заглушку с безопасным поведением.
Проверка на nil защищает только от полностью nil-интерфейса. Она не обнаруживает интерфейс, внутри которого находится типизированный nil-указатель, поэтому при необходимости нужно отдельно продумать контракт конкретного типа.
Сервис принимает необязательный интерфейс логирования. Если оставить его nil и вызвать метод при обработке ошибки, основная операция завершится паникой вместо нормального возврата ошибки.
Можно проверять интерфейс перед каждым вызовом, но это увеличивает количество условной логики и риск пропустить проверку. Можно передавать реализацию-заглушку: вызовы безопасны, однако легко скрыть ошибку конфигурации.
Практичнее выбрать один из вариантов на границе системы: либо конструктор отклоняет nil-зависимость с понятной ошибкой, либо подставляет явно документированную заглушку. Тогда внутренний код получает гарантированный контракт и не содержит повторяющихся проверок.
Да, если динамический тип интерфейса — указатель на конкретный тип. Go найдёт метод по этому динамическому типу и начнёт его выполнение. Метод с указательным получателем может сам проверить получатель на nil, но обращение к его полям без проверки приведёт к панике.
Она вернёт false, потому что интерфейс не пуст: в нём сохранён динамический тип указателя. Сравнение с nil проверяет состояние всего интерфейсного значения, а не nil-состояние объекта, на который указывает его динамическое значение.
Обычная форма assertion завершится паникой, поскольку у интерфейса нет динамического типа, к которому можно выполнить утверждение. Форма с результатом ok безопаснее: она вернёт нулевое значение целевого типа и false, позволяя обработать отсутствие значения без паники.