Esse erro em tempo de compilação ocorre quando você tenta atribuir ou passar (ou converter) um tipo concreto para um tipo de interface; e o próprio tipo não implementa a interface, apenas um ponteiro para o tipo .
Vamos ver um exemplo:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
O Stringer
tipo de interface tem apenas um método: String()
. Qualquer valor armazenado em um valor de interface Stringer
deve ter esse método. Também criamos a MyType
, e criamos um método MyType.String()
com receptor de ponteiro . Isso significa que o String()
método está no conjunto de métodos do *MyType
tipo, mas não no de MyType
.
Quando tentamos atribuir um valor de MyType
a uma variável do tipo Stringer
, obtemos o erro em questão:
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)
Mas está tudo bem se tentarmos atribuir um valor do tipo *MyType
para Stringer
:
s = &m
fmt.Println(s)
E obtemos o resultado esperado (experimente no Go Playground ):
something
Portanto, os requisitos para obter esse erro em tempo de compilação:
- Um valor do tipo concreto não-ponteiro sendo atribuído (ou passado ou convertido)
- Um tipo de interface que está sendo atribuído (ou passado a, ou convertido em)
- O tipo concreto possui o método necessário da interface, mas com um receptor de ponteiro
Possibilidades de resolver o problema:
- Um ponteiro para o valor deve ser usado, cujo conjunto de métodos incluirá o método com o receptor do ponteiro
- Ou o tipo de receptor deve ser alterado para não ponteiro , para que o conjunto de métodos do tipo concreto não ponteiro também contenha o método (e, portanto, satisfaça a interface). Isso pode ou não ser viável, como se o método tivesse que modificar o valor, um receptor sem ponteiro não é uma opção.
Estruturas e incorporação
Ao usar estruturas e incorporação , geralmente não é "você" que implementa uma interface (fornece uma implementação de método), mas um tipo que você incorpora no seu struct
. Como neste exemplo:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Mais uma vez, erro em tempo de compilação, porque o conjunto de métodos MyType2
não contém o String()
método incorporado MyType
, apenas o conjunto de métodos *MyType2
, portanto, o seguinte funciona (tente no Go Playground ):
var s Stringer
s = &m2
Também podemos fazê-lo funcionar, se incorporarmos *MyType
e usarmos apenas um não ponteiro MyType2
(experimente no Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Além disso, o que quer que incorporemos ( MyType
ou *MyType
), se usarmos um ponteiro *MyType2
, ele sempre funcionará (tente no Go Playground ):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Seção relevante da especificação (da seção Tipos de estrutura ):
Dado um tipo de estrutura S
e um tipo chamado T
, os métodos promovidos são incluídos no conjunto de métodos da estrutura, da seguinte maneira:
- Se
S
contiver um campo anônimo T
, os conjuntos de métodos de S
e *S
ambos incluirão métodos promovidos com o receptor T
. O conjunto de *S
métodos também inclui métodos promovidos com o receptor *T
.
- Se
S
contiver um campo anônimo *T
, os conjuntos de métodos de S
e *S
ambos incluirão métodos promovidos com receptor T
ou *T
.
Então, em outras palavras: se incorporarmos um tipo não ponteiro, o conjunto de métodos do incorporador não ponteiro só obtém os métodos com receptores não ponteiros (do tipo incorporado).
Se incorporarmos um tipo de ponteiro, o conjunto de métodos do incorporador não-ponteiro obterá métodos com receptores de ponteiro e não-ponteiro (do tipo incorporado).
Se usarmos um valor de ponteiro para o incorporador, independentemente de o tipo incorporado ser ponteiro ou não, o conjunto de métodos do ponteiro para o incorporador sempre obtém métodos com os receptores de ponteiro e não-ponteiro (do tipo incorporado).
Nota:
Existe um caso muito semelhante, a saber, quando você tem um valor de interface que envolve um valor MyType
e tenta digitar afirmar outro valor de interface Stringer
,. Nesse caso, a afirmação não será válida pelos motivos descritos acima, mas obtemos um erro de tempo de execução ligeiramente diferente:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Pânico no tempo de execução (experimente no Go Playground ):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Tentando converter em vez de declarar tipo, obtemos o erro em tempo de compilação sobre o qual estamos falando:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
func (m *MyType)
" ou nenhum . É assim? Posso misturar diferentes tipos de "funções-membro", por exemplo,func (m *MyType)
&func (m MyType)
?