Interfaces in Go
By Flavio Copes
An introduction to interfaces in Go, a type that defines method signatures so any struct implementing those methods can be used through the interface.
An interface in Go is a type that defines one or more method signatures.
Methods are not implemented, just their signature: the name, parameter types and return value type.
Something like this:
type Speaker interface {
Speak()
}
Now you could have a function accept any type that implements all the methods defined by the interface:
func SaySomething(s Speaker) {
s.Speak()
}
And we can pass it any struct that implements those methods:
package main
import "fmt"
type Speaker interface {
Speak()
}
type Person struct {
Name string
Age int
}
func (p Person) Speak() {
fmt.Println("Hello from " + p.Name)
}
func SaySomething(s Speaker) {
s.Speak()
}
func main() {
flavio := Person{Age: 39, Name: "Flavio"}
SaySomething(flavio)
}
Running this prints:
Hello from Flavio
Implementation is implicit
Here’s the part that surprises people coming from other languages: there is no implements keyword.
Person never declares that it satisfies Speaker. It just has a Speak() method with the right signature, and that’s enough. Go checks this at compile time.
This changes how you design code. You can define an interface in your package and have types from other packages satisfy it, even packages written before your interface existed. The type’s author doesn’t need to know about you.
Why do interfaces exist?
Interfaces let a function care about behavior instead of concrete types.
SaySomething doesn’t know or care that it received a Person. Tomorrow you can add a Robot struct with its own Speak() method, and SaySomething accepts it with zero changes.
The standard library leans on this everywhere. fmt.Stringer is an interface with a single String() method, and any type implementing it gets custom printing in fmt.Println(). io.Writer has a single Write() method, and it’s why the same code can write to a file, a network connection, or a buffer.
Notice both have one method. That’s idiomatic Go: small interfaces, satisfied by many types.
Watch out for pointer receivers
One pitfall bites everyone eventually. If the method is defined on a pointer receiver, only the pointer satisfies the interface:
func (p *Person) Speak() {
fmt.Println("Hello from " + p.Name)
}
func main() {
flavio := Person{Age: 39, Name: "Flavio"}
SaySomething(flavio) //compile error
SaySomething(&flavio) //works
}
With func (p *Person) Speak(), passing the value flavio fails to compile: Person does not implement Speaker. The fix is passing a pointer, &flavio, or defining the method on a value receiver like in the first example.