Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interfaces and pointer receivers

I am newbie gopher and trying to get my head around the pointer receivers and interfaces.

type Foo interface {
    foo()
}
type Bar struct {}
func (b *Bar) foo() {}

based on the above definitions..

--- Allowed ---------
b := Bar{}
b.foo()

--- Not allowed ----- 

var foo Foo = Bar{}

Get compiler error: cannot use Bar literal (type Bar) as type Foo in assignment: Bar does not implement Foo (foo method has pointer receiver)

I understand that compiler is doing some pointer conversion and de-referencing on our behalf in the first scenario. Why doesn't it do the same thing in the second scenario ?

like image 990
gopher Avatar asked Aug 12 '17 16:08

gopher


2 Answers

Short answer var foo Foo = Bar{} is not working because the concrete value stored in an interface is not addressable.

Longer Version

Please read https://github.com/golang/go/wiki/MethodSets

It is legal to call a pointer-valued method on anything that is already a pointer or whose address can be taken. It is legal to call a value method on anything which is a value or whose value can be dereferenced.

With respect to the above explanation, your code

b := Bar{}
b.foo()

works because b is addressable.

The concrete value stored in an interface is not addressable. Therefore, when you call a method on an interface, it must either have an identical receiver type or it must be directly discernible from the concrete type: pointer- and value-receiver methods can be called with pointers and values respectively, as you would expect. Value-receiver methods can be called with pointer values because they can be dereferenced first. Pointer-receiver methods cannot be called with values, however, because the value stored inside an interface has no address. When assigning a value to an interface, the compiler ensures that all possible interface methods can actually be called on that value, and thus trying to make an improper assignment will fail on compilation.

According to the above explanation the concrete value stored in an interface is not addressable and hence the code,

var foo Foo = Bar{}

will not work because the concrete value stored in an interface, in this case Bar{}, is not addressable.

like image 198
Naveen Ramanathan Avatar answered Sep 18 '22 15:09

Naveen Ramanathan


The explanation lies in the fact that when dealing with the concrete struct itself, it has the proper information to handle this automatically. You can read in the tour here that:

Go automatically handles conversion between values and pointers for method calls.

But when you are dealing with an interface{} type, it has less information on what is actually contained in the variable. It just knows there is a foo() method. But there is a subtlety here that requires extra explanation so here is an example.

https://play.golang.org/p/Y0fJcAISw1

type Foo interface {
    foo()
}
type Bar struct {}
func (b *Bar) foo() {}

type Baz struct {}
func (b Baz) foo() {}

func main() {
    b := Bar{}
    b.foo()

    var v Foo = &Bar{}
    // v = Bar{} // fails
    v.foo()

    v = Baz{}
    v.foo()
    v = &Baz{} // works too
    v.foo()
}

Notice that &Baz{} works even though it has a value receiver, but not the reverse. The reason being that a *Baz points to exactly one Baz, both of which exist (the pointer and the value), so the value is easily obtained. When you try to do v = Bar{}, the value exists, but the pointer does not, and Go will not automatically create one for an interface{} value.

This is all explained in detail under the Pointers and interfaces heading in this blog post

like image 38
RayfenWindspear Avatar answered Sep 19 '22 15:09

RayfenWindspear