Go Interfaces and Pointers
The Go programming language has an interesting take on pointers. While pointers exist, they are often hidden or dereferenced automatically. This behavior can be confusing around interfaces. Go doesn't use pointers to interfaces. So when you are working with something that is an interface, you have to be aware of what the underlying objects are, without refering to them as pointers to interfaces.
Suppose we start with an interface.
type Hello interface {
SayHello()
}
In order to use the interface, we simply refer to it as a type. For example, we might create a function like this:
func TellThemHello(hello Hello) {
hello.SayHello()
}
You can't use a pointer to the interface.
func TellThemHello(hello *Hello) { //This won't work
hello.SayHello()
}
If you try, you will get an error.
src/interfacePointer/main.go:38: cannot use helloPointer (type *MyPointerHello) as type *Hello in argument to TellThemHello:
*Hello is pointer to interface, not interface
For a type to implement the interface it has to define the SayHello()
method. So let's create a custom type that implements Hello as a pointer.
type MyPointerHello struct {
Name string
}
func (self *MyPointerHello) SayHello() {
fmt.Printf("Hello %v\n", self.Name)
}
Notice that SayHello()
expects a pointer to a MyPointerHello
. If we try to pass a non-pointer, or value, as the interface, it won't work, because the non-pointer doesn't implment the method. For example, if we try to compile:
var helloPointer MyPointerHello //This should be a pointer
helloPointer.Name = "From A Pointer"
TellThemHello(helloPointer)
The compiler will fail with the error.
src/interfacePointer/main.go:39: cannot use helloPointer (type MyPointerHello) as type Hello in argument to TellThemHello:
MyPointerHello does not implement Hello (SayHello method has pointer receiver)
However, if we use a pointer to a MyPointerHello we can compile the program error free.
var helloPointer = new(MyPointerHello)
helloPointer.Name = "From A Pointer"
TellThemHello(helloPointer)
The same is true from the other other side. If you define a type that implements Hello
as a value.
type MyStackHello struct {
Name string
}
func (self MyStackHello) SayHello() {
fmt.Printf("Hello %v\n", self.Name)
}
You use it as a value.
var helloOnStack MyStackHello
helloOnStack.Name = "From The Stack"
TellThemHello(helloOnStack)
Combining these use cases we can do:
func main() {
var helloOnStack MyStackHello
var helloPointer = new(MyPointerHello)
helloOnStack.Name = "From The Stack"
helloPointer.Name = "From A Pointer"
TellThemHello(helloOnStack)
TellThemHello(helloPointer)
}
Compiling and running this code gives us the following output.
Hello From The Stack
Hello From A Pointer
Now you might think you can just define SayHello()
for both. But this is not allowed, and represents a redeclaration.
# command-line-arguments
src/interfacePointer/main.go:25: method redeclared: MyStackHello.SayHello
method(MyStackHello) func()
method(*MyStackHello) func()
So, to sum it all up:
- Don't use pointers to interfaces
- Implement the interface on a value or a pointer, and use it that way
The code for this example is available on github in the interfacePointer folder.