79

I have this example code

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 does not work as expected because its receiver is not of pointer type.

If I change the method to a pointer receiver, what I would expect to work, it looks like this:

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

Compiling this leads to the following error:

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

How can I have the struct implement the interface and the method SetSomeField change the value of the actual instance without creating a copy?

Here's a hackable snippet: https://play.golang.org/p/ghW0mk0IuU

I've already seen this question In go (golang), how can you cast an interface pointer into a struct pointer?, but I cannot see how it is related to this example.

0

2 Answers 2

87

Your pointer to the struct should implement the Interface. In that way you can modify its fields.

Look at how I modified your code, to make it working as you expect:

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())
}
Sign up to request clarification or add additional context in comments.

Comments

42

The simple answer is that you won't be able to have the struct implement your interface while having SetSomeField work the way you want.

However, a pointer to the struct will implement the interface, so changing your Create method to do return &obj should get things working.

The underlying problem is that your modified SetSomeField method is no longer in the method set of Implementation. While the type *Implementation will inherit the non-pointer receiver methods, the reverse is not true.

The reason for this is related to the way interface variables are specified: the only way to access the dynamic value stored in an interface variable is to copy it. As an example, imagine the following:

var impl Implementation
var iface IFace = &impl

In this case, a call to iface.SetSomeField works because it can copy the pointer to use as the receiver in the method call. If we directly stored a struct in the interface variable, we'd need to create a pointer to that struct to complete the method call. Once such a pointer is made, it is possible to access (and potentially modify) the interface variable's dynamic value without copying it.

3 Comments

thanks james for sharing! I guess this should be the accepted answer as provides a more detailed answer of the internals of interfaces and struct's pointer receiver methods.
"If we directly stored a struct in the interface variable, we'd need to create a pointer to that struct to complete the method call." - Not true actually. Here is an example: play.golang.org/p/v3Rl3l0MKZ7
@UdayHiwarale: your example includes no interface variables. See this error: play.golang.org/p/0ABWJlxC_oQ

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.