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 ?
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.
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
Half of your question depends on whether your value is addressable or not:
For an operand
x
of typeT
, the address operation&x
generates a pointer of type*T
tox
. The operand must be addressable, that is, either:
- a variable,
- pointer indirection, or
- slice indexing operation; or
- a field selector of an addressable struct operand; or
- an array indexing operation of an addressable array.
As an exception to the addressability requirement,
x
may also be a (possibly parenthesized) composite literal.
Bar{}
is a composite literal, thus it is not addressable. You may type &Bar{}
to create an object of type *Bar
, but that is listed as "an exception to the addressability requirement", reinforcing the idea that Bar{}
is not addressable in itself.
Variable b
of type Bar
can invoke b.foo()
despite Bar.foo()
requiring a pointer receiver for a good reason:
A method call
x.m()
is valid if the method set of (the type of)x
containsm
and the argument list can be assigned to the parameter list ofm
. Ifx
is addressable and&x
's method set containsm
,x.m()
is shorthand for(&x).m()
— Calls
However, that does not mean Bar.foo()
is in the method set of b
. This is because b
has type Bar
while Bar.foo()
receives a value of type *Bar
:
A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type
T
consists of all methods declared with receiver typeT
. The method set of the corresponding pointer type*T
is the set of all methods declared with receiver*T
orT
(that is, it also contains the method set ofT
).— from Method sets
Because the method sets of b
and the Foo
interface differ, you cannot use var foo Foo = b
, despite b.foo()
being converted to (&b).foo()
by the compiler. Otherwise, var foo Foo = Bar{}
would work. You may, however, use either of the following since Bar.foo()
receives a *Bar
:
var foo Foo = &b
var foo Foo = &Bar{}
来源:https://stackoverflow.com/questions/45652560/interfaces-and-pointer-receivers