In go, is it backwards compatible to change a receiver struct from a value to a pointer?

87 Views Asked by At

Starting with code like:

type Foo struct {
    V bool
}

func (f Foo) bar() bool {
    return f.V
}

Is it allowed to change to func (f *Foo) bar() bool without incrementing the major version number? That is, assuming you know there are no thread safety issues with your type. If so, the reverse change is allowed too, correct?

Any code that called the function regardless of whether the variable was a value or a pointer would continue to compile and work as expected after both changes, as far as I can tell.

2

There are 2 best solutions below

0
Brits On BEST ANSWER

As per the comments, this answer answer provides a good summary T vs *T receiver types. Following are a couple of examples where a change from func (f Foo) bar() bool to func (f *Foo) bar() bool would break existing code.

Example 1: T is not addressable (playground):

type Foo struct {
    V bool
}

func (f *Foo) bar() bool { // Change to `(f Foo)` and this will work
    return f.V
}

func x() Foo {
    return Foo{true}
}

func main() {
    fmt.Printf("%v", x().bar())
}

Example 2: Interface (playground):

type Foo struct {
    V bool
}

func (f *Foo) bar() bool { // Change to `(f Foo)` and this will work
    return f.V
}

type Fooer interface {
    bar() bool
}

func main() {
    var x Fooer
    x = Foo{}
    fmt.Println(x.bar())
}
1
Burak Serdar On

If for a type Foo you have a method with value receiver:

func (f Foo) Method() {...}

Then this method is defined for both Foo and *Foo, and for both cases, the method receives a copy of the instance of Foo. If you change this to :

func (f *Foo) Method() {...}

then Foo.Method is only available for *Foo, and not for Foo. Thus, such a change may cause compile errors.

If you have a method declared with pointer receiver and now you change that to a value receiver, you declare the method for both Foo and *Foo, so you should not have compile errors. The semantics of the method will also change, because now Method will receive a copy of the instance of Foo even if that instance is addressable, or it is a pointer.