Cette erreur de compilation se produit lorsque vous essayez d'affecter ou de passer (ou de convertir) un type concret à un type d'interface; et le type lui-même n'implémente pas l'interface, seulement un pointeur vers le type .
Voyons un exemple:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Le Stringer
type d'interface a une seule méthode: String()
. Toute valeur stockée dans une valeur d'interface Stringer
doit avoir cette méthode. Nous avons également créé un MyType
, et nous avons créé une méthode MyType.String()
avec récepteur de pointeur . Cela signifie que la String()
méthode se trouve dans l' ensemble de méthodes du *MyType
type, mais pas dans celui de MyType
.
Lorsque nous essayons d'attribuer une valeur de MyType
à une variable de type Stringer
, nous obtenons l'erreur en question:
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)
Mais tout va bien si nous essayons d'attribuer une valeur de type *MyType
à Stringer
:
s = &m
fmt.Println(s)
Et nous obtenons le résultat attendu (essayez-le sur le Go Playground ):
something
Donc, les exigences pour obtenir cette erreur de compilation:
- Une valeur de type concret non pointeur étant affectée (ou passée ou convertie)
- Un type d'interface affecté à (ou transmis à, ou converti en)
- Le type concret a la méthode requise de l'interface, mais avec un récepteur de pointeur
Possibilités de résoudre le problème:
- Un pointeur vers la valeur doit être utilisé, dont l'ensemble de méthodes inclura la méthode avec le récepteur de pointeur
- Ou le type de récepteur doit être changé en non-pointeur , donc l'ensemble de méthodes du type concret non-pointeur contiendra également la méthode (et satisfera ainsi l'interface). Cela peut être viable ou non, car si la méthode doit modifier la valeur, un récepteur sans pointeur n'est pas une option.
Structures et encastrement
Lorsque vous utilisez des structures et l'incorporation , ce n'est souvent pas «vous» qui implémentez une interface (fournissez une implémentation de méthode), mais un type que vous intégrez dans votre struct
. Comme dans cet exemple:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Encore une fois, erreur au moment de la compilation, car l'ensemble de méthodes de MyType2
ne contient pas la String()
méthode de l'incorporé MyType
, uniquement l'ensemble de méthodes de *MyType2
, donc les opérations suivantes fonctionnent (essayez-les sur le Go Playground ):
var s Stringer
s = &m2
Nous pouvons également le faire fonctionner, si nous incorporons *MyType
et utilisons uniquement un non-pointeur MyType2
(essayez-le sur le Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
De plus, quoi que nous incorporions (soit MyType
ou *MyType
), si nous utilisons un pointeur *MyType2
, cela fonctionnera toujours (essayez-le sur le Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Section pertinente de la spécification (de la section Types de structures ):
Étant donné un type de structure S
et un type nommé T
, les méthodes promues sont incluses dans l'ensemble de méthodes de la structure comme suit:
- Si
S
contient un champ anonyme T
, les ensembles de méthodes de S
et les *S
deux incluent des méthodes promues avec le récepteur T
. L'ensemble de *S
méthodes comprend également des méthodes promues avec récepteur *T
.
- Si
S
contient un champ anonyme *T
, les ensembles de méthodes de S
et les *S
deux incluent des méthodes promues avec le récepteur T
ou *T
.
En d'autres termes: si nous incorporons un type sans pointeur, l'ensemble de méthodes de l'embedder sans pointeur obtient uniquement les méthodes avec des récepteurs sans pointeur (du type incorporé).
Si nous incorporons un type pointeur, l'ensemble de méthodes de l'embedder non pointeur obtient des méthodes avec des récepteurs pointeurs et non pointeurs (du type incorporé).
Si nous utilisons une valeur de pointeur vers l'embedder, que le type incorporé soit pointeur ou non, l'ensemble de méthodes du pointeur vers l'embedder obtient toujours des méthodes avec les récepteurs pointeurs et non pointeurs (du type embarqué).
Remarque:
Il existe un cas très similaire, à savoir lorsque vous avez une valeur d'interface qui encapsule une valeur de MyType
, et que vous essayez de taper assert une autre valeur d'interface à partir de celle-ci Stringer
,. Dans ce cas, l'assertion ne tiendra pas pour les raisons décrites ci-dessus, mais nous obtenons une erreur d'exécution légèrement différente:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Panique à l'exécution (essayez-le sur le Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Tentative de conversion au lieu de type assert, nous obtenons l'erreur de compilation dont nous parlons:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
", ou aucun . En est-il ainsi? Puis-je mélanger différents types de "fonctions membres", par exemple,func (m *MyType)
&func (m MyType)
?