Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Types of Go struct methods that satisfy an interface

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?

like image 992
danze Avatar asked Dec 15 '22 02:12

danze


1 Answers

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?

like image 149
icza Avatar answered Jan 22 '23 22:01

icza