260 votes

X n'implémente pas Y (... la méthode a un pointeur récepteur)

Il y a déjà plusieurs questions Et réponses sur ce "X ne permet pas de mettre en œuvre des Y (... méthode a un pointeur récepteur)" chose, mais pour moi, ils semble être de parler de choses différentes, et ne pas l'appliquer à mon cas précis.

Donc, au lieu de faire de la question très spécifique, je suis en train de faire de larges et abstrait -- Semble comme il y a plusieurs cas qui peuvent faire cette erreur arrive, quelqu'un peut-il résumé, il s'il vous plaît?

I. e., comment éviter le problème, et si elle se produit, quelles sont les possibilités? Thx.

448voto

icza Points 3857

Cette erreur de compilation se produit lorsque vous essayez de céder ou de transmettre (ou convertir) un béton de type pour 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 }

L' Stringer type d'interface a une méthode uniquement: String(). Toute valeur qui est stockée dans une interface valeur Stringer doit avoir cette méthode. Nous avons également créé un MyType, et nous avons créé une méthode MyType.String() avec pointeur récepteur. Cela signifie que l' String() méthode est la méthode de l' *MyType type, mais pas dans celui de la MyType.

Lorsque l'on tente d'attribuer une valeur de MyType pour 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 est ok si nous essayons d'affecter une valeur de type *MyType de Stringer:

s = &m
fmt.Println(s)

Et nous obtenons le résultat attendu (essayez sur la Aller de l'aire de Jeux):

something

Donc, les exigences pour obtenir cette erreur de compilation:

  • Une valeur de non-pointeur de type concret d'être affecté (ou passé, ou converti)
  • Un type d'interface étant assigné (ou passé, ou convertis)
  • Le type de béton nécessaire de la méthode de l'interface, mais avec un pointeur récepteur

Possibilités pour résoudre le problème:

  • Un pointeur vers la valeur doit être utilisé, dont la méthode d'ensemble comprendra la méthode avec le pointeur de récepteur
  • Ou le type du récepteur doit être changé pour non-pointeur, de sorte que la méthode de la non-pointeur de type de béton contiendra également la méthode (et de répondre ainsi à l'interface). Cela peut ou peut ne pas être viable, comme si la méthode a pour modifier la valeur, un non-pointeur récepteur n'est pas une option.

Les structures et l'incorporation de

Lors de l'utilisation des structures et de l'intégration, il n'est souvent pas "vous" qui implémentent une interface (fournir une méthode de mise en œuvre), mais un type de vous intégrer 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 de compilation, car la méthode de MyType2 ne contient pas l' String() méthode de l'embedded MyType, seule la méthode de *MyType2, de sorte que les œuvres suivantes (essayez sur la Aller de l'aire de Jeux):

var s Stringer
s = &m2

Nous pouvons également le faire fonctionner, si nous intégrer *MyType et en utilisant seulement un non-pointeur MyType2 (essayez sur la Aller de l'aire de Jeux):

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = m2

Aussi, tout ce que nous intégrer ( MyType ou *MyType), si on utilise un pointeur *MyType2, il le sera toujours (essayer sur les Aller de l'aire de Jeux):

type MyType2 struct {
    *MyType
}

m := MyType{value: "something"}
m2 := MyType2{MyType: &m}

var s Stringer
s = &m2

Section pertinente de la spec (à partir de la section Struct types):

Étant donné un type struct S et un type nommé T, promu méthodes sont inclus dans la méthode d'ensemble de la structure comme suit:

  • Si S contient un anonyme, un champ T, la méthode des ensembles de S et *S à la fois inclure le promu méthodes avec récepteur T. La méthode de *S comprend également promu méthodes avec récepteur *T.
  • Si S contient un anonyme, un champ *T, la méthode des ensembles de S et *S à la fois inclure le promu méthodes avec récepteur T ou *T.

Donc, en d'autres mots: si nous rajoutons un non-type de pointeur, la méthode de la non-pointeur embedder ne reçoit que les méthodes non-pointeur de récepteurs (à partir de l'embedded type).

Si nous rajoutons un type pointeur, la méthode de la non-pointeur embedder obtient des méthodes à la fois avec le pointeur et non-pointeur de récepteurs (à partir de l'embedded type).

Si l'on utilise une valeur de pointeur à l'embedder, indépendamment du fait que l'intégré est de type pointeur ou pas, la méthode du pointeur à l'embedder obtient toujours des méthodes à la fois avec le pointeur et non-pointeur de récepteurs (à partir de l'embedded type).

Note:

Il est très similaire, à savoir lorsque vous disposez d'une interface de valeur qui encapsule une valeur de MyType, et vous essayez de type affirmer une autre interface de valeur, Stringer. Dans ce cas, l'affirmation sera pas pour les raisons décrites ci-dessus, mais nous sommes un peu différents à l'exécution d'erreur:

m := MyType{value: "something"}

var i interface{} = m
fmt.Println(i.(Stringer))

Exécution de panique (essayez sur la Aller de l'aire de Jeux):

panic: interface conversion: main.MyType is not main.Stringer:
    missing method String

De tenter de les convertir à la place du type affirmer, nous obtenons l'erreur de compilation que nous parlons:

m := MyType{value: "something"}

fmt.Println(Stringer(m))

47voto

Saman Shafigh Points 1573

Garder court, disons que vous avez ce code, et vous avez un Chargeur d'interface et un WebLoader qui implémente cette interface.

package main

import "fmt"

// Loader defines a content loader
type Loader interface {
    Load(src string) string
}

// WebLoader is a web content loader
type WebLoader struct{}

// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
    return fmt.Sprintf("I loaded this page %s", src)
}

func main() {
    webLoader := WebLoader{}
    loadContent(webLoader)
}

func loadContent(loader Loader) {
    loader.Load("google.com")
}

Donc, ce code vous donne cette erreur de compilation

./principal.aller:20:13: impossible d'utiliser webLoader (type WebLoader) type de Chargeur en argument à loadContent: WebLoader ne pas mettre en œuvre Chargeur (méthode Load a pointeur récepteur)

Donc, ce que vous avez seulement besoin de faire est de changer webLoader := WebLoader{} à la suivante:

webLoader := &WebLoader{} 

Alors pourquoi il va corriger parce que vous définissez cette fonction func (w *WebLoader) Load d'accepter un pointeur récepteur. Pour plus d'explications, veuillez lire @icza et @karora réponses

6voto

karora Points 462

Un autre cas, quand j'ai vu ce genre de chose qui se passe est que si je veux créer une interface où certaines méthodes de modifier une valeur interne et d'autres pas.

type GetterSetter interface {
   GetVal() int
   SetVal(x int) int
}

Quelque chose qui met ensuite en place cette interface pourrait être:

type MyTypeA struct {
   a int
}

func (m MyTypeA) GetVal() int {
   return a
}

func (m *MyTypeA) SetVal(newVal int) int {
    int oldVal = m.a
    m.a = newVal
    return oldVal
}

Ainsi, le type d'implémentation aura probablement quelques méthodes qui sont le pointeur de récepteurs et certains qui ne le sont pas et depuis que j'ai tout à fait une variété de choses différentes qui sont GetterSetters je tiens à le consulter dans mes tests, qu'ils sont tous ce que l'on attend.

Si je devais faire quelque chose comme ceci:

myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
    t.Fail()
}

Je n'irai pas le fameux "X ne permet pas de mettre en œuvre des Y (Z méthode a pointeur récepteur)" erreur (car c'est une erreur de compilation), mais je vais avoir une mauvaise journée de traquer exactement pourquoi mon test est un échec...

Au lieu de cela, je dois m'assurer que je ne la vérification de type à l'aide d'un pointeur, tels que:

var f interface{} = new(&MyTypeA)
 ...

Ou:

myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...

Alors tout est heureux avec les tests!

Mais attendez! Dans mon code, j'ai peut-être des méthodes qui acceptent un GetterSetter quelque part:

func SomeStuff(g GetterSetter, x int) int {
    if x > 10 {
        return g.GetVal() + 1
    }
    return g.GetVal()
}

Si j'appelle ces méthodes à partir de l'intérieur d'un autre type de méthode, cela va générer l'erreur:

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

L'une des demandes suivantes:

func (m *MyTypeA) OtherThing(x int) {
    SomeStuff(m, x)
}

func (m MyTypeA) OtherThing(x int) {
    SomeStuff(&m, x)
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X