Questo errore di compilazione si verifica quando si tenta di assegnare o passare (o convertire) un tipo concreto a un tipo di interfaccia; e il tipo stesso non implementa l'interfaccia, solo un puntatore al tipo .
Vediamo un esempio:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Il Stringertipo di interfaccia ha un solo metodo: String(). Qualsiasi valore memorizzato in un valore di interfaccia Stringerdeve avere questo metodo. Abbiamo anche creato un MyTypee abbiamo creato un metodo MyType.String()con il ricevitore puntatore . Ciò significa che il String()metodo è nel set di metodi del *MyTypetipo, ma non in quello di MyType.
Quando proviamo ad assegnare un valore MyTypea una variabile di tipo Stringer, otteniamo l'errore in questione:
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
Ma è tutto ok se proviamo ad assegnare un valore di tipo *MyTypea Stringer:
s = &m
fmt.Println(s)
E otteniamo il risultato atteso (provalo sul Go Playground ):
something
Quindi i requisiti per ottenere questo errore di compilazione:
- Un valore di tipo concreto non puntatore assegnato (o passato o convertito)
- Un tipo di interfaccia assegnato (o passato o convertito in)
- Il tipo concreto ha il metodo richiesto dell'interfaccia, ma con un ricevitore puntatore
Possibilità di risolvere il problema:
- È necessario utilizzare un puntatore al valore, il cui set di metodi includerà il metodo con il ricevitore puntatore
- In alternativa, il tipo di ricevitore deve essere modificato in non puntatore , quindi il set di metodi del tipo concreto non puntatore conterrà anche il metodo (e quindi soddisfare l'interfaccia). Ciò può essere o non essere fattibile, come se il metodo dovesse modificare il valore, un ricevitore senza puntatore non è un'opzione.
Strutture e incorporamento
Quando si utilizzano le strutture e l'incorporamento , spesso non è "tu" l'implementazione di un'interfaccia (fornire un'implementazione del metodo), ma un tipo che incorpori nel tuo struct. Come in questo esempio:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Ancora una volta, errore di compilazione, perché il set di metodi di MyType2non contiene il String()metodo del incorporato MyType, solo il set di metodi di *MyType2, quindi il seguente funziona (provalo sul Play Goground ):
var s Stringer
s = &m2
Possiamo anche farlo funzionare, se incorporiamo *MyTypee utilizziamo solo un non puntatore MyType2 (provalo nel Play Goground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Inoltre, qualunque cosa incorporiamo (o MyTypeo *MyType), se utilizziamo un puntatore *MyType2, funzionerà sempre (provalo nel Play Goground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Sezione pertinente delle specifiche (dalla sezione Tipi di strutture ):
Dato un tipo di struttura Se un tipo denominato T, i metodi promossi sono inclusi nel set di metodi della struttura come segue:
- Se
Scontiene un campo anonimo T, i set di metodi di Sed *Sentrambi includono metodi promossi con ricevitore T. Il set di *Smetodi include anche metodi promossi con ricevitore *T.
- Se
Scontiene un campo anonimo *T, il metodo set di Se *Sentrambi includono metodi con ricevitore promosso To *T.
Quindi, in altre parole: se incorporiamo un tipo non puntatore, l'insieme di metodi dell'embedder non puntatore ottiene solo i metodi con ricevitori non puntatore (dal tipo incorporato).
Se incorporiamo un tipo di puntatore, il set di metodi dell'embedder senza puntatore ottiene metodi con entrambi i ricevitori puntatore e non puntatore (dal tipo incorporato).
Se utilizziamo un valore di puntatore per l'embedder, indipendentemente dal fatto che il tipo incorporato sia puntatore o meno, l'insieme di metodi del puntatore all'embedder ottiene sempre metodi con entrambi i ricevitori puntatore e non puntatore (dal tipo incorporato).
Nota:
C'è un caso molto simile, vale a dire quando si dispone di un valore di interfaccia che racchiude un valore di MyTypee si tenta di digitare affermare un altro valore di interfaccia da esso Stringer,. In questo caso l'asserzione non sarà valida per i motivi sopra descritti, ma si ottiene un errore di runtime leggermente diverso:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Panico di runtime (provalo sul Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Tentando di convertire anziché digitare assert, otteniamo l'errore di compilazione di cui stiamo parlando:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)" o nessuna . È così? Posso mescolare diversi tipi di "funzioni membro", ad es.func (m *MyType)&func (m MyType)?