Suraj Deshmukh

Blog

containers, programming, golang, hacks, kubernetes, productivity, books

Methods that satisfy interfaces in golang

What receiver type methods satisfy which interface, can be understood here.

Suraj Deshmukh

3-Minute Read

Pointer receiver

For a struct User with a method Work with pointer receiver.

type User struct {
    Name   string
    Period int
}

func (u *User) Work() {
    fmt.Println(u.Name, "has worked for", u.Period, "hrs.")
}

func main() {
    uval := User{"UserVal", 5}
    uval.Work()

    pval := &User{"UserPtr", 6}
    pval.Work()
}

See on go playground.

output:

UserVal has worked for 5 hrs.
UserPtr has worked for 6 hrs.

If we call this method on value type object uval it works, and obviously it works with pointer type object pval.

Value receiver

Now we change the method receiver from pointer to value.

type User struct {
    Name   string
    Period int
}

func (u User) Work() {
    fmt.Println(u.Name, "has worked for", u.Period, "hrs.")
}

func main() {
    uval := User{"UserVal", 5}
    uval.Work()

    pval := &User{"UserPtr", 6}
    pval.Work()
}

See on go playground.

output:

UserVal has worked for 5 hrs.
UserPtr has worked for 6 hrs.

So this also worked on both value type object uval and with pointer type object pval.

Interface and pointer receiver

Lets try to add interface in the mix and see what happens, so adding interface Worker to pointer receiver method:

type User struct {
    Name   string
    Period int
}

type Worker interface {
    Work()
}

func (u *User) Work() {
    fmt.Println(u.Name, "has worked for", u.Period, "hrs.")
}

func main() {
    uval := User{"UserVal", 5}
    DoWork(uval)

    pval := &User{"UserPtr", 6}
    DoWork(pval)
}

func DoWork(w Worker) {
    w.Work()
}

See on go playground.

output:

# command-line-arguments
tmp/main.go:20:8: cannot use uval (type User) as type Worker in argument to DoWork:
        User does not implement Worker (Work method has pointer receiver)

So pointer type object pval implements interface Worker, but value type object uval does not. Since the error clearly says

User does not implement Worker (Work method has pointer receiver)

To understand why this above code fails, you need to understand the concept of method sets. Golang spec defines Method sets as:

The method set of any other type T consists of all methods declared with receiver type T.

and

The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).

finally

The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.

In above example the method with pointer receiver is not in method set of value type object uval.

Interface and value receiver

Now lets try the same inteface but this time with value receiver:

type User struct {
    Name   string
    Period int
}

type Worker interface {
    Work()
}

func (u User) Work() {
    fmt.Println(u.Name, "has worked for", u.Period, "hrs.")
}

func main() {
    uval := User{"UserVal", 5}
    DoWork(uval)

    pval := &User{"UserPtr", 6}
    DoWork(pval)
}

func DoWork(w Worker) {
    w.Work()
}

See on go playground.

output:

UserVal has worked for 5 hrs.
UserPtr has worked for 6 hrs.

Meanwhile the method with value receiver worked for both type of objects pointer type object pval and value type object uval.

So here is a table which charts this behavior:

scenario object value object pointer
pointer method yes yes
value method yes yes
interface pointer method no yes
interface value method yes yes

More on the case of why code compilation fails or compiler complains about value type object uval not implementing the interface Worker.

References

comments powered by Disqus

Recent Posts

Categories

About

I am a Senior Software Engineer at Microsoft, working on various tooling around container technology like Docker, Kubernetes, etc.