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)
?MyType
, se você não pode mudarMyType
para usar métodos de valor. Eu tentei algo assim,(&i).(*Stringer)
mas não está funcionando. Isso é possível?x := i.(MyType)
, e então pode chamar métodos com o receptor de ponteiro, por exemploi.String()
, que é uma abreviação para a(&i).String()
qual é bem-sucedida porque as variáveis são endereçáveis. Mas o método do ponteiro que altera o valor (o valor apontado) não será refletido no valor envolvido no valor da interface, por isso faz pouco sentido.*T
não estão incluídos no conjunto de métodosS
porqueS
podem não ser endereçáveis (por exemplo, valor de retorno da função ou resultado da indexação de mapas) e também porque geralmente apenas uma cópia está presente / recebida e, se o uso do endereço for permitido, o método com o ponteiro receptor só pode modificar a cópia (confusão como você supõe que o original seja modificado). Veja esta resposta para um exemplo: Usando reflexão SetString .Para mantê-lo breve, digamos que você tenha esse código e tenha uma interface do Loader e um WebLoader que implementem essa interface.
Portanto, este código fornecerá esse erro em tempo de compilação
Então, o que você só precisa fazer é mudar
webLoader := WebLoader{}
para o seguinte:Então, por que isso será corrigido porque você define esta função
func (w *WebLoader) Load
para aceitar um receptor de ponteiro. Para mais explicações, leia as respostas @icza e @karorafonte
Outro caso em que vejo esse tipo de coisa acontecendo é se eu quero criar uma interface em que alguns métodos modifiquem um valor interno e outros não.
Algo que implementa essa interface pode ser como:
Portanto, o tipo de implementação provavelmente terá alguns métodos que são receptores de ponteiro e outros que não são, e como tenho várias dessas coisas que são GetterSetters, gostaria de verificar nos meus testes se todos estão fazendo o esperado.
Se eu fizesse algo assim:
Então eu não vou conseguir o já mencionado "X não implementar Y (método Z tem receptor ponteiro)" erro (uma vez que é um erro em tempo de compilação), mas eu vai ter um dia ruim perseguir exatamente por isso que o meu teste está falhando .. .
Em vez disso, tenho que me certificar de fazer a verificação de tipo usando um ponteiro, como:
Ou:
Então tudo está feliz com os testes!
Mas espere! No meu código, talvez eu tenha métodos que aceitem um GetterSetter em algum lugar:
Se eu chamar esses métodos de dentro de outro método de tipo, isso gerará o erro:
Qualquer uma das seguintes chamadas funcionará:
fonte