Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused with implicit pointer dereference when assigning a pointer to interface in Go

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?

like image 986
LeckieNi Avatar asked Jun 26 '15 05:06

LeckieNi


2 Answers

Your last sentence:

"Since b is a pointer type to Integer, 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.

like image 150
icza Avatar answered Nov 15 '22 09:11

icza


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.

like image 33
hobbs Avatar answered Nov 15 '22 08:11

hobbs