0

I am trying to understand Golang (1.12) interfaces. I found out that interface pointers must be explicitly dereferenced, unlike structs:

import "fmt"

// A simple interface with one function
type myinter interface {
    hello()
}

// Implement myinter
type mystruct struct {}

func (mystruct) hello() {
    fmt.Println("I am T!")
}

// Some function that calls the hello function as defined by myinter
func callHello(i *myinter) {   
    i.hello()  // <- cannot resolve reference 'hello'
}

func main() {
    newMystruct := &mystruct{}
    callHello(newMystruct)
}

Here, my callHello function cannot resolve the reference to my hello function as defined in the interface. Of course, dereferencing the interface works:

func callHello(i *myinter) {   
    (*i).hello()  // <- works!
}

But in structs, we can call the function directly, none of this cumbersome dereference notation is necessary:

func callHello(s *mystruct) {   
    s.hello()  // <- also works!
}

Why is this the case? Is there a reason why we must explicitly dereference interface pointers? Is Go trying to discourage me from passing interface pointers into functions?

2
  • 3
    It's rare to use a pointer to an interface in Go. Nothing in the question indicates that there's a reason to use a pointer to an interface. Fix by changing *myinter to myinter. This fixes the error at callHello(newMystruct) and at i.hello(). Commented Oct 9, 2019 at 17:41
  • 1
    Possible duplicate of Interface can not call self method, Commented Oct 9, 2019 at 18:13

3 Answers 3

1

It is related to the way the type system works. An interface type I defines a method set. The method set is defined for type I, and not for type *I. Because of this, the use of *I is limited. It can be used when a function is to set the interface value, but it is rare:

func f(x *error) {
  *x = fmt.Errorf("Some error")
}

Note that the interface itself can have an underlying pointer value:

func f(x someInterface) {
   *x.(*someType) = value
}

func main() {
   x := someType{}
   f(&x)
}

This is different for non-interface types. When you define a method for a non-interface type T, the method is defined for both T and *T. If you define a method for *T, it is only defined for *T, and not for T.

Sign up to request clarification or add additional context in comments.

2 Comments

But if I want to take an interface as a parameter in a function, wouldn't it be inefficient to pass a concrete type in by value? Interfaces represent abstract types, wouldn't passing a pointer be better than passing by value especially when we don't know the size of the concrete type?
If you pass an interface to a function, what's passed is the pointer to the underlying value, and a pointer to the type. If the underlying value of the interface is a struct (not pointer), interface points to a copy of the struct. If you pass a concrete struct to the function, the struct will be copied. If you pass a pointer to the struct, only a pointer will be passed. So passing in a struct takes a copy, regardless if you pass it via an interface or not.
1

Is Go trying to discourage me from passing interface pointers into functions?

Yes.

Pointer-to-interface is wrong in (almost*) all cases.

(* There are very rare and delicate cases where pointer-to-interface makes sense but chances are high you won't see them more than once every 5 years.)

(Nitpick: The language is called "Go". "golang.org" is the website. Basics like this are covered by the compatibility promise and do not depend on the version: Go 1.0, 1.12 and 1.16 behave exactly the same in this regard.)

2 Comments

Can you elaborate why this is wrong? Coming from a heavy Java development past, it seems that interface pointers would be more common (passing pointers to abstract types).
@KennyWorden Pointer-to-something is needed if you want to mutate the something. An interface value itself is seldomly modified. What is modified is the actual value contained in the interface value: It is common to store pointers inside an interface, but that is completely different from a pointer to an interface value itself.
0

Simply remove the pointer and use the interface.

func callHello(i myinter) {   
    i.hello()  
}

This func will now accept either an instance of a struct that implements the interface, or a pointer to an instance of a struct that implements the interface.

func main() {
    instance := mystruct{}
    pointer := &mystruct{}
    callHello(instance)
    callHello(pointer)
}

outputs the following:

I am T!
I am T!
 
Program exited.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.