4

Given the following Go code example:

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var t2 *tourGuide = &tourGuide{"Smith"}
    t2.hello()   // Hello Smith
    t2.goodbye() // Goodbye Smith (same as (*t2).hello())

    // illegal: t1 is not assignable to g1 (why?)
    // var g1 greeter = t1

    var g2 greeter = t2
    g2.hello()   // Hello Smith
    g2.goodbye() // Goodbye Smith
}

I'm able to call the two methods of the struct tourGuide using either a variable of type tourGuide t1 or a pointer to tourGuide t2. In other words, I can call a method with T receiver using a variable of type T or *T. Similarly, I can call a method with *T receiver using a variable of type T (if T is addressable) or *T. I understand that the compiler handles the differences here (see my comments in the code).

However, things change when we are implementing interfaces. In the above code, a variable of type greeter interface is assignable from a pointer to tourGuide but not from a tourGuide.

Can anyone tell me why this is the case? Why am I able to call t1.hello() and t1.goodbye() but somehow t1 is not enough for the interface greeter?

icza
  • 389,944
  • 63
  • 907
  • 827
danze
  • 745
  • 2
  • 10
  • 24
  • Possible duplicate of [Go, X does not implement Y (… method has a pointer receiver)](http://stackoverflow.com/questions/40823315/go-x-does-not-implement-y-method-has-a-pointer-receiver/40824044#40824044) – icza Dec 07 '16 at 10:37
  • Thanks. But my question is, why does the compiler allows to call struct methods with value and pointer receivers using value types and pointers. But doesn't provide similar support to interfaces? May be its more of a design question than how the language works? – danze Dec 07 '16 at 10:57
  • @danze If this is a design question than it is not appropriate for SO. – Volker Dec 07 '16 at 12:02
  • @Volker My problem originated from not completely understanding Go interfaces and the accepted answer clarified that for me. It's not a design question. – danze Dec 07 '16 at 20:50

3 Answers3

8

If a method has a pointer receiver, only a pointer value can be used as the receiver value. So to call this method on some value, the value itself must be a pointer, or it must be possible to acquire its address (to be used as the receiver).

If you have a variable for example, it is addressable and thus it's possible to get its address and use that as the receiver. And the spec allows you to do this, this happens automatically.

Values wrapped in interfaces are not addressable. When an interface value is created, the value that is wrapped in the interface is copied. It is therefore not possible to take its address. Theoretically you could allow to take the address of the copy, but that would be the source of (even) more confusion than what benefit it would provide, as the address would point to a copy, and methods with pointer receiver could only modify the copy and not the original.

See this answer which details / proves that values are copied when the interface value is created: How can a slice contain itself?

Community
  • 1
  • 1
icza
  • 389,944
  • 63
  • 907
  • 827
  • This is what I was after. Now I understand why the compiler is behaving this way. Thanks! – danze Dec 07 '16 at 11:50
3

if you have a pointer to a struct then go will allow you to access the properties on the struct and its functions which have value type receivers (as apposed to pointer receivers) without having to dereference your pointer, but this only works for one level of pointer, see your code below where I turn t2 into a pointer to a pointer to a tourguide, at this point I need to explicitly dereference it to make it back into a pointer to a tourguide. think of the first level of a pointer to a struct as being a special case that go allows you to use syntatic sugar to access the value types properties and functions to save you having to constantly manually dereference you variables.

package main

import "fmt"

type greeter interface {
    hello()
    goodbye()
}

type tourGuide struct {
    name string
}

func (t tourGuide) hello() {
    fmt.Println("Hello", t.name)
}

func (t *tourGuide) goodbye() {
    fmt.Println("Goodbye", t.name)
}

func main() {
    var t1 tourGuide = tourGuide{"James"}
    t1.hello()   // Hello James
    t1.goodbye() // Goodbye James (same as (&t1).goodbye())

    var tmpT2 *tourGuide = &tourGuide{"Smith"}
    var t2 **tourGuide = &tmpT2
    (*t2).hello()   // Hello Smith
    (*t2).goodbye() // Goodbye Smith (same as (*t2).hello())

    //illegal: t1 is not assignable to g1 (why?)
    //var g1 greeter = t1

    //now this is illegal too
    //var g2 greeter = t2

    var g3 greeter = (*t2)
    g3.hello()   // Hello Smith
    g3.goodbye() // Goodbye Smith
}
Daniel Robinson
  • 13,806
  • 18
  • 64
  • 112
1

My answer here explains why Go prevents you from taking the address of a value stored in an interface.

tl;dr its because a pointer to A which points to a value of type A in an interface would be invalidated when a value of different type B is subsequently stored in the interface.

WPWoodJr
  • 1,155
  • 1
  • 12
  • 16
  • 1
    Up voting because the answer that you linked to (which is also written by you) is really good. Still, I think this should be reworded to "My answer here" to make it clear that the other answer has also been written by you. – kapad Aug 26 '20 at 14:00