Le interfacce in Go sono un insieme di metodi che un tipo deve implementare per corrispondere a quell'interfaccia. Non esiste una parola chiave esplicita implements: la compatibilità è strutturale, non dichiarativa. L'assegnazione di un tipo implementante a una variabile di tipo interfaccia è possibile solo se il tipo implementa tutti i metodi dell'interfaccia.
Importante: la variabile interfaccia contiene due puntatori: ai dati (value) e al tipo (type).
Esempio di dichiarazione e implementazione:
type Printer interface { Print() } type MyPrinter struct{} func (mp MyPrinter) Print() { fmt.Println("printing...") } var p Printer = MyPrinter{} p.Print() // "printing..."
Cosa succede se la variabile interfaccia è uguale a nil? In cosa differisce "var i interface{} = nil" da "var i interface{}"?
Risposta errata frequente — "entrambi i valori sono nil". In realtà — no:
var i interface{} = nil — la variabile è effettivamente nil (type=nil, value=nil)var p *MyPrinter = nil; var i Printer = p, allora i != nil, perché type != nil (all'interno di i — type=*MyPrinter, value=nil), e molte verifiche come if i == nil non funzioneranno quando ci si aspetta.Esempio:
var p *MyPrinter = nil var i Printer = p fmt.Println(i == nil) // false!
Storia
Descrizione: In un servizio, il gestore degli errori restituiva un'interfaccia con valore nil, e i clienti pensavano che l'errore fosse non nullo, causando azioni superflue. Il problema era nel confronto dell'interfaccia con nil.
Storia
Descrizione: Durante la scrittura dei test, si controllava erroneamente l'errore di tipo interfaccia su uguaglianza a nil dopo il ritorno di una struttura con campi nil. I test non rilevavano errori reali, portando a un bug in produzione.
Storia
Descrizione: Durante la migrazione da un tipo interfaccia a un altro, si dimenticò di implementare tutti i metodi della nuova interfaccia, poiché non c'era un esplicito implements. Il codice compilava, ma l'interfaccia non veniva implementata, alcune funzioni mock avevano smesso di funzionare.