Este error en tiempo de compilación surge cuando intenta asignar o pasar (o convertir) un tipo concreto a un tipo de interfaz; y el tipo en sí no implementa la interfaz, solo un puntero al tipo .
Veamos un ejemplo:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
El Stringer
tipo de interfaz tiene un solo método: String()
. Cualquier valor almacenado en un valor de interfaz Stringer
debe tener este método. También creamos a MyType
, y creamos un método MyType.String()
con receptor de puntero . Esto significa que el String()
método está en el conjunto de métodos del *MyType
tipo, pero no en el de MyType
.
Cuando intentamos asignar un valor de MyType
a una variable de tipo Stringer
, obtenemos el error en cuestión:
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)
Pero todo está bien si intentamos asignar un valor de tipo *MyType
a Stringer
:
s = &m
fmt.Println(s)
Y obtenemos el resultado esperado (pruébalo en Go Playground ):
something
Entonces, los requisitos para obtener este error en tiempo de compilación:
- Un valor de tipo concreto sin puntero que se asigna (o se pasa o se convierte)
- Un tipo de interfaz que se asigna (o se pasa o se convierte)
- El tipo concreto tiene el método requerido de la interfaz, pero con un receptor de puntero
Posibilidades para resolver el problema:
- Se debe utilizar un puntero al valor, cuyo conjunto de métodos incluirá el método con el receptor del puntero
- O bien, el tipo de receptor debe cambiarse a sin puntero , por lo que el conjunto de métodos del tipo concreto sin puntero también contendrá el método (y así satisfará la interfaz). Esto puede o no ser viable, ya que si el método tiene que modificar el valor, un receptor sin puntero no es una opción.
Estructuras e incrustaciones
Cuando se utilizan estructuras e incrustaciones , a menudo no es "usted" quien implementa una interfaz (proporcione una implementación de método), sino un tipo que incrusta en su struct
. Como en este ejemplo:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
De nuevo, error en tiempo de compilación, porque el conjunto de métodos de MyType2
no contiene el String()
método de incrustado MyType
, solo el conjunto de métodos de *MyType2
, por lo que funciona lo siguiente (pruébelo en Go Playground ):
var s Stringer
s = &m2
También podemos hacer que funcione, si incrustamos *MyType
y usamos solo un no puntero MyType2
(pruébalo en Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Además, lo que sea que incrustamos (ya sea MyType
o *MyType
), si usamos un puntero *MyType2
, siempre funcionará (pruébalo en Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Sección relevante de la especificación (de la sección Tipos de estructura ):
Dado un tipo de estructura S
y un tipo con nombre T
, los métodos promocionados se incluyen en el conjunto de métodos de la estructura de la siguiente manera:
- Si
S
contiene un campo en el anonimato T
, los conjuntos de métodos de S
y *S
ambos incluyen métodos con el receptor promovido T
. El conjunto de *S
métodos también incluye métodos promocionados con receptor *T
.
- Si
S
contiene un campo anónimo *T
, los conjuntos de método de S
y *S
ambos incluyen métodos con receptor promovido T
o *T
.
En otras palabras: si incrustamos un tipo sin puntero, el conjunto de métodos del incrustador sin puntero solo obtiene los métodos con receptores sin puntero (del tipo incrustado).
Si incrustamos un tipo de puntero, el conjunto de métodos del incrustador sin puntero obtiene métodos con receptores punteros y no punteros (del tipo incrustado).
Si usamos un valor de puntero para el integrador, independientemente de si el tipo incrustado es puntero o no, el conjunto de métodos del puntero para el integrador siempre obtiene métodos con receptores punteros y no punteros (del tipo incrustado).
Nota:
Existe un caso muy similar, es decir, cuando tiene un valor de interfaz que envuelve un valor de MyType
e intenta escribir afirmar otro valor de interfaz a partir de él Stringer
,. En este caso, la afirmación no se mantendrá por los motivos descritos anteriormente, pero obtenemos un error de tiempo de ejecución ligeramente diferente:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Pánico en tiempo de ejecución (pruébalo en Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Intentando convertir en lugar de escribir aserción, obtenemos el error en tiempo de compilación del que estamos hablando:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
" o ninguno . ¿Es tan? ¿Puedo mezclar diferentes tipos de "funciones miembro", por ejemplo,func (m *MyType)
&func (m MyType)
?