25 вопросов
Интерфейс - именованный набор сигнатур методов. Тип реализует интерфейс неявно (не нужно объявлять implements): если у типа есть все методы интерфейса, он ему удовлетворяет. Переменная типа интерфейса может хранить значение любого конкретного типа с этими методами.
iface - интерфейс с методами: указатель на таблицу методов (itable) и указатель на данные. eface - пустой интерфейс interface{}: указатель на тип и указатель на данные. itable связывает конкретный тип с таблицей методов интерфейса.
interface{} или any (Go 1.18+) - интерфейс без методов. Ему удовлетворяет любой тип. Используется для универсальных контейнеров и функций, принимающих любое значение. Для извлечения типа нужна type assertion или type switch.
Интерфейс равен nil только если и тип, и значение внутри nil (и указатель на тип, и указатель на данные нулевые). Если в интерфейс записан ненулевой тип и nil значение (например, *MyType(nil)), интерфейс не nil - сравнение if err != nil может дать true для "логически" nil ошибки.
Переменная интерфейса хранит (тип, значение). Если присвоить указатель nil конкретного типа, в интерфейсе будет (тип, nil). if x == nil тогда false - интерфейс не nil. Для ошибок это типично: возвращать return err где err имеет тип, но значение nil. Решение: возвращать явно nil или проверять через errors.Is.
Type assertion: v := x.(T) - извлечь значение типа T из интерфейса; при неудаче паника. Безопасная форма: v, ok := x.(T). Type switch: switch x.(type) { case T1: ... case T2: ... } - ветвление по типу значения в интерфейсе.
В Go принято определять интерфейс со стороны потребителя: маленький интерфейс с нужными методами в пакете, который использует зависимость. Так зависимость не импортирует "чужой" пакет, и интерфейс остаётся минимальным. Крупные интерфейсы в пакете с реализацией - реже.
Да. Присваивание значения конкретного типа переменной типа интерфейса компилируется только если тип реализует все методы интерфейса. Явной декларации "implements" нет - компилятор проверяет наличие методов. Невыполнение контракта даёт ошибку компиляции.
В Go не нужно объявлять, что тип реализует интерфейс (no implements). Интерфейс определяет только методы, не поля. Любой тип может удовлетворить интерфейс без наследования. Интерфейсы обычно маленькие (1-3 метода). Структуры могут удовлетворять нескольким интерфейсам без явной иерархии.
"Если ходит как утка и крякает - это утка". В Go это неявная реализация интерфейса: тип не объявляет интерфейс, но если у него есть нужные методы - он подходит. Компилятор проверяет это статически. Часто говорят "structural typing" для интерфейсов.
В пустой: любое значение присваивается var x any = value. Обратно: type assertion v := x.(ConcreteType) или v, ok := x.(ConcreteType). Для проверки типа - type switch. При неверном утверждении типа - паника (без ok) или ok == false.
Встроенное ограничение comparable (Go 1.18+) - типы, которые можно сравнивать с ==/!=. Используется в дженериках: func [K comparable, V any]. Интерфейсы сравниваемы: сравниваются тип и значение внутри. Слайсы, map, функции - не comparable.
С Go 1.18 any - встроенный алиас для interface{}. Семантически одно и то же. any короче и читается как "любой тип". Рекомендуется использовать any в новом коде.
Type assertion: v := x.(*MyStruct) или v, ok := x.(*MyStruct). Type switch: switch v := x.(type) { case *MyStruct: ... case int: ... }. Для ошибок - errors.As для извлечения типа ошибки из цепочки.
Пустой интерфейс - когда нужна произвольная величина (сериализация, логирование, контейнеры). Интерфейс с методами - когда важен контракт (чтение/запись, сравнение, сортировка). Предпочтительно маленькие интерфейсы с нужными методами вместо any.
Абстракция без наследования: подмена реализаций (тесты, моки), dependency inversion. Один тип может удовлетворять разным интерфейсам. Полиморфизм: функция принимает интерфейс и работает с любым типом с нужными методами. Меньшая связность кода.
io.Reader - интерфейс с методом Read(p []byte) (n int, err error). io.Writer - Write(p []byte) (n int, err error). Базовые интерфейсы для ввода-вывода: файлы, сеть, буферы реализуют их. Композиция через io.Reader/Writer позволяет универсально обрабатывать потоки.
Интерфейс для сортировки: Len() int, Less(i, j int) bool, Swap(i, j int). Тип с этими методами можно передать в sort.Sort. Для слайсов чаще используют sort.Slice с функцией сравнения, не реализуя полный интерфейс.
Method set типа для интерфейса - набор методов, которые должны быть у типа, чтобы он удовлетворял интерфейсу. У интерфейса тоже есть method set - перечень методов. Тип реализует интерфейс, если его method set содержит метод set интерфейса (для указателей учитываются методы с pointer receiver).
Определить свой тип-обёртку (или алиас до 1.9) и объявить для него методы. Либо обёртка-структура: type MyInt int; func (m MyInt) Foo() {}. К чужому типу из другого пакета методы добавить нельзя - только свой тип на его основе.
Интерфейс fmt.Stringer с методом String() string. Если тип реализует String(), fmt.Print, fmt.Sprintf("%v", x) используют его для строкового представления. "Stringer" - тип, реализующий этот интерфейс.
Переменная ошибки может иметь тип (например, *MyError), но значение nil. Тогда интерфейс (тип, значение) не nil, и err != nil истинно. Нужно возвращать return nil вместо return err когда err nil, либо проверять через errors.Is(err, nil) или по типу errors.As.
Интерфейс может включать другой интерфейс по имени (встраивание): type ReadWriter interface { Reader; Writer }. Результирующий интерфейс требует все методы встроенных. Дублирование методов не допускается. Удобно для композиции контрактов.
Нет. В интерфейсе только методы. Поля задаются в структурах. Если нужны и "поля", и поведение - интерфейс с методами (в том числе геттеры), а данные в конкретном типе.
При присваивании значения конкретного типа переменной типа интерфейса значение копируется в heap (если не указатель), а в интерфейс кладётся пара (тип, указатель на это значение или само значение для малых). Боксинг - упаковка значения в интерфейс, возможное выделение памяти. Указатели в интерфейсе не боксят сам объект - копируется указатель.