Интерфейсы в Go — это набор методов, которые должен реализовать тип для соответствия этому интерфейсу. Нет явного ключевого слова implements: совместимость структурная, а не декларативная. Присваивание реализующего типа переменной интерфейсного типа возможно только если тип реализует все методы интерфейса.
Важно: интерфейсная переменная содержит два указателя — на данные (value) и на тип (type).
Пример объявления и реализации:
type Printer interface { Print() } type MyPrinter struct{} func (mp MyPrinter) Print() { fmt.Println("printing...") } var p Printer = MyPrinter{} p.Print() // "printing..."
Что произойдет, если интерфейсная переменная равна nil? Чем отличается "var i interface{} = nil" от "var i interface{}"?
Частый неверный ответ — "оба значения — nil". На самом деле — нет:
var i interface{} = nil — переменная действительно nil (type=nil, value=nil)var p *MyPrinter = nil; var i Printer = p, то i != nil, потому что type != nil (внутри i — type=*MyPrinter, value=nil), и многие проверки наподобие if i == nil не сработают, когда ожидаете.Пример:
var p *MyPrinter = nil var i Printer = p fmt.Println(i == nil) // false!
История
Описание: В одном сервисе обработчик ошибок возвращал интерфейс с nil-значением, и клиенты считали ошибку ненулевой, вызывая избыточные действия. Проблема была в сравнении интерфейса с nil.
История
Описание: При написании тестов ошибочно проверяли ошибку интерфейсного типа на равенство nil после возврата структуры с nil-полями. Тесты не детектировали настоящие ошибки, что привело к появлению бага на проде.
История
Описание: При миграции с одного интерфейсного типа на другой забыли реализовать все методы нового интерфейса, т.к. не было явного implements. Код компилировался, но интерфейс не реализовывался, некоторые мокающиеся функции перестали работать.