Подтвердить что ты не робот

Метод Голанга с приемником указателя

У меня есть код примера

package main

import (
    "fmt"
)

type IFace interface {
    SetSomeField(newValue string)
    GetSomeField() string
}

type Implementation struct {
    someField string
}

func (i Implementation) GetSomeField() string {
    return i.someField
}

func (i Implementation) SetSomeField(newValue string) {
    i.someField = newValue
}

func Create() IFace {
    obj := Implementation{someField: "Hello"}
    return obj // <= Offending line
}

func main() {
    a := Create()
    a.SetSomeField("World")
    fmt.Println(a.GetSomeField())
}

SetSomeField работает не так, как ожидалось, потому что его приемник не имеет тип указателя.

Если я изменю метод на приемник указателя, что бы я ожидал работать, он выглядит так:

func (i *Implementation) SetSomeField(newValue string) { ...

Компиляция приводит к следующей ошибке:

prog.go:26: cannot use obj (type Implementation) as type IFace in return argument:
Implementation does not implement IFace (GetSomeField method has pointer receiver)

Как я могу < <24 > реализовать интерфейс и метод SetSomeField изменить значение фактического экземпляра без создания копии?

Здесь взломанный фрагмент: https://play.golang.org/p/ghW0mk0IuU

Я уже видел этот вопрос В go (golang), как вы можете нарисовать указатель на указатель структуры?, но я не вижу, как это происходит связанные с этим примером.

4b9b3361

Ответ 1

Ваш указатель на структуру должен реализовать интерфейс. Таким образом вы можете изменить свои поля.

Посмотрите, как я изменил ваш код, чтобы он работал так, как вы ожидаете:

package main

import (
    "fmt"
)

type IFace interface {
    SetSomeField(newValue string)
    GetSomeField() string
}

type Implementation struct {
    someField string
}    

func (i *Implementation) GetSomeField() string {
    return i.someField
}

func (i *Implementation) SetSomeField(newValue string) {
    i.someField = newValue
}

func Create() *Implementation {
    return &Implementation{someField: "Hello"}
}

func main() {
    var a IFace
    a = Create()
    a.SetSomeField("World")
    fmt.Println(a.GetSomeField())
}

Ответ 2

Простой ответ заключается в том, что у вас не будет возможности реализовать ваш интерфейс, когда SetSomeField работает так, как вы хотите.

Однако указатель на структуру реализует интерфейс, поэтому изменение метода Create для выполнения return &obj должно заставить все работать.

Основная проблема заключается в том, что ваш модифицированный метод SetSomeField больше не находится в наборе методов Implementation. Пока тип *Implementation наследует методы приемника не указателя, обратное неверно.

Причина этого связана с тем, как указаны интерфейсные переменные: единственный способ получить доступ к динамическому значению, хранящемуся в переменной интерфейса, - это его копирование. В качестве примера представьте себе следующее:

var impl Implementation
var iface IFace = &impl

В этом случае вызов iface.SetSomeField работает, потому что он может скопировать указатель для использования в качестве приемника в вызове метода. Если мы непосредственно сохранили структуру в переменной интерфейса, нам нужно создать указатель на эту структуру для завершения вызова метода. После создания такого указателя можно (и возможно модифицировать) динамическое значение переменной интерфейса без копирования.