I am new to Go, and I am studying its interface feature.
Here is the code:
package main
import (
"fmt"
"reflect"
)
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
var a Integer = 1
var b LessAdder = &a
func main() {
fmt.Println(reflect.TypeOf(b))
fmt.Println(b.Less(2))
b.Add(a)
fmt.Println(a)
}
And it will output the the following:
*main.Integer
true
2
Well, this works pretty well.
The Point is:
How var b LessAdder = &a
works. Does the pointer auto-dereference happens right here, or when b invokes member method?
The output *main.Integer
tells us that b is a pointer to type Integer
, hence it is the second case.
Then the tricky thing comes:
when I add fmt.Pringln(*b)
to the code, the compiler comes with an error:
demo/demo1
./demo1.go:31: invalid indirect of b (type LessAdder)
And it confuses me. Since b
is a pointer type to Integer
, then dereferencing it should work. But why not?
Your last sentence:
"Since
b
is a pointer type toInteger
, then dereferencing it should work."
Stop right there. b
is not a variable of pointer type and therefore you can't dereference it.
It is a variable of interface type which is schematically a pair of a value and a type (value,type), holding &a
as the value and *Integer
as the type (blog article The Laws of Reflection, section The representation of an interface).
This is a declaration of a variable of pointer type, *Integer
:
var ip *Integer
And this is one of an interface type:
var intf LessAdder
When you do this:
var b LessAdder = &a
What happens is that an interface value (of type LessAdder
) is created automatically/implicitly which will hold the value &a
(and the type *Integer
). This is a valid operation because the type of &a
(which is *Integer
) implements the interface LessAdder
: the method set of *Integer
is a superset of the interface LessAdder
(in this case they are equal, the method set of an interface type is its interface).
Now when you call b.Less(2)
, since Less()
has a value receiver, the pointer will be dereferenced and a copy of the pointed value will be made and used/passed as the value receiver of the method Less()
.
fmt.Println(reflect.TypeOf(b))
doesn't lie, but it will print the dynamic type of b
. The dynamic type of b
is indeed *Integer
, but the static type of b
is LessAdder
and the static type is what determines what you can do with a value and what operators or methods are allowed on it.
LessAdder
is declared as an interface with the methods Less
and Add
. Since Add
is declared with a receiver of *Integer
, a *Integer
can be a LessAdder
; an Integer
can't. When you do var b LessAdder = &a
, it's the pointer to a
that's stored in the interface b
.
The automatic indirection occurs at the call to b.Less(2)
, because both methods on *Integer
and methods on Integer
contribute to the method set of *Integer
.
You can't use *b
because although b
contains a *Integer
, statically its type is LessAdder
, not *Integer
. Leaving aside the representation of interfaces, LessAdder
isn't a pointer type, and *b
, if it was allowed, would have no expressible type at all.
You can use a type assertion to access b
as an Integer *
again; b.(*Integer)
is an expression of type *Integer
, and *b.(*Integer)
is an Integer
. Both of these will run-time panic if the value in b
is not a *Integer
after all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With