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 Stringer
tipo di interfaccia ha un solo metodo: String()
. Qualsiasi valore memorizzato in un valore di interfaccia Stringer
deve avere questo metodo. Abbiamo anche creato un MyType
e abbiamo creato un metodo MyType.String()
con il ricevitore puntatore . Ciò significa che il String()
metodo è nel set di metodi del *MyType
tipo, ma non in quello di MyType
.
Quando proviamo ad assegnare un valore MyType
a 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 *MyType
a 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 MyType2
non 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 *MyType
e 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 MyType
o *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 S
e un tipo denominato T
, i metodi promossi sono inclusi nel set di metodi della struttura come segue:
- Se
S
contiene un campo anonimo T
, i set di metodi di S
ed *S
entrambi includono metodi promossi con ricevitore T
. Il set di *S
metodi include anche metodi promossi con ricevitore *T
.
- Se
S
contiene un campo anonimo *T
, il metodo set di S
e *S
entrambi includono metodi con ricevitore promosso T
o *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 MyType
e 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)
?