Как вывести только часть информации из интерфейсов в Go
Что проверяют
Формулировка размытая, и интервьюер ждёт, что вы её уточните. Обычно за ней стоит одно из двух: как через интерфейс получить доступ только к нужной части поведения типа и как управлять тем, что тип показывает при печати. Следом спрашивают про type assertion и про ловушку с nil-интерфейсом.
Как отвечать
Интерфейс в Go это набор методов, и через значение интерфейса видно ровно то, что он объявляет. Отсюда несколько инструментов:
- узкий интерфейс: функция принимает не всю структуру, а маленький интерфейс, например
io.Readerвместо*os.File. Это прямая идиома Go: «принимайте интерфейсы, возвращайте структуры»; - type assertion:
v, ok := x.(Named)проверяет, умеет ли значение больше, чем объявлено. Форма сokне паникует; - type switch:
switch v := x.(type)разбирает несколько конкретных типов или интерфейсов; fmt.Stringer: методString() stringопределяет, что выведут%vи%s, и позволяет скрыть лишние поля, например пароль. Для%#vестьGoStringer;slog.LogValuer: то же для структурных логов, методLogValue()отдаёт в лог только безопасные поля;- теги структур: при сериализации
json:"-"исключает поле, аomitemptyпропускает нулевые значения.
Полезно показать, что значение интерфейса хранит пару «тип, значение», поэтому интерфейс с типизированным nil внутри сам по себе не равен nil.
Где спотыкаются
Вызывают fmt.Sprintf("%v", u) внутри метода String() того же типа и получают бесконечную рекурсию. Внутри нужно форматировать поля по отдельности или привести значение к типу без методов.
Объявляют String() с получателем-указателем, а печатают значение. В набор методов типа User не входят методы с получателем *User, и fmt выведет сырые поля вместе с паролем.
Пишут x.(T) без второго значения там, где тип не гарантирован. Неудачная проверка вызывает панику, и без recover падает вся программа.