Для чего используется рефлексия в Go
Что проверяют
Понимаете ли вы, какую задачу решает пакет reflect и почему в Go его стараются избегать. Следом обычно спрашивают, как encoding/json читает теги структур и чем рефлексию можно заменить после появления дженериков.
Как отвечать
Рефлексия позволяет во время выполнения узнать тип значения, лежащего в интерфейсе, и работать с ним, не зная тип на этапе компиляции. Основа пакета:
reflect.TypeOfиreflect.ValueOf: достают тип и значение изany. У типа естьKind(),NumField(),Field(i), у поля структурыTag.Get("json");- изменение значений:
Valueдолжен быть адресуемым, поэтому передают указатель и берутElem().CanSet()вернётfalseдля неэкспортируемых полей и для значения, переданного копией; - вызов методов:
MethodByNameиCall, на этом построены DI-контейнеры вродеuber/dig.
Где это реально используется: encoding/json и encoding/xml раскладывают данные по полям согласно тегам, fmt печатает любое значение через %v, go-playground/validator читает теги validate, sqlx сканирует строки в структуры, в тестах есть reflect.DeepEqual.
Дальше назовите цену. Ошибки, которые компилятор поймал бы сразу, превращаются в панику во время выполнения. Код медленнее: лишние аллокации, вызовы не инлайнятся. Читать его тяжелее. Альтернативы: дженерики с Go 1.18 для обобщённых алгоритмов, switch по типу для конечного набора типов, кодогенерация через go generate (stringer, mockgen, sqlc, easyjson), которая переносит работу на этап сборки.
Где спотыкаются
Вызывают reflect.ValueOf(s).Field(0).SetInt(1) на структуре, переданной по значению, и получают панику про неадресуемое значение. Нужен reflect.ValueOf(&s).Elem().
Удивляются, что json.Marshal не видит поле name. Неэкспортируемые поля рефлексией не записываются и не отдаются наружу, поэтому стандартная библиотека их пропускает.
Не проверяют IsValid(): reflect.ValueOf(nil) возвращает нулевой Value, и большинство методов на нём паникуют. Сюда же путаница между nil-интерфейсом и интерфейсом, внутри которого лежит nil-указатель: второй не равен nil.