I have an issue with Go: Interfaces are implemented implicitly.
You implement an interface by implementing all methods of that interface. However, you don't mention that you are implementing it.
type Speaker interface {
Speak() string
}
type Cat struct {}
func (c Cat) Speak() string {
return "meow"
}
func Announce(s Speaker) {
fmt.Println("Behold, for it speaks: ", s.Speak())
}
func main() {
cat := Cat{}
Announce(cat)
}
This is fine and works as you would expect. What happens if you don't implement the interface correctly?
// ↓ Whoops, typo
func (c Cat) Speek() string { … }
Trying to run this will result in a compiler error.
./prog.go:21:11:
cannot use cat (variable of type Cat) as Speaker value in argument to Announce:
Cat does not implement Speaker (missing method Speak)
The error is immediately noticeable and easy enough to fix. However, this is only a tiny example.
In a bigger real-life application, main
, Announce
, Cat
, and Speaker
would all be located in different files in different packages.
The compiler would still complain that the error is in main
.
This is technically correct.
main
wants to Announce
a Speaker
,
but Cat
isn't one.
However, the mistake happened much earlier.
I wrote Cat
and wanted it to implement Speaker
,
except I made a mistake.
The compiler should direct me towards fixing the method I implemented on Cat
and not tell me that I cannot use Cat
in main
.
The Solution
Hidden away in a short paragraph in the Go FAQ lies the solution to this problem:
You can ask the compiler to check that the type T implements the interface I by attempting an assignment using the zero value for T:
var _ I = T{} // Verify that T implements I.
Applied to the previous example it would look like this.
type Cat struct {}
var _ Speaker = Cat{}
func (c Cat) Speek() string {…}
In addition to the previous error message, you now get another error.
./prog.go:11:17:
cannot use Cat{} (value of type Cat) as Speaker value in variable declaration:
Cat does not implement Speaker (missing method Speak)
The error points to the line var _ Speaker = Cat{}
,
right next to the method I want to implement!
Brilliant.
This is especially useful with an IDE
that lets you quickly jump to compile errors.
Instead of hunting through files trying to figure this out, the Go compiler now tells me exactly where I need to fix my mistake.
Expressing Intent
Another bonus is that my intent is more clear.
I did not implement a Speak
method by accident
and just so happened to fulfill the Speaker
interface.
I did it deliberately.
var _ Speaker = Cat{}
tells other programmers
(which always includes yourself in the future)
that I want Cat
to implement Speaker
.
The syntax is a little weird, but you can get used to it.
However, I would prefer something like
impl Speaker for Cat
,
which would be easier to understand.
The next time you are implementing an interface I
for your struct T
,
consider making the implementation explicit by writing var _ I = T{}
.
This clarifies your intent
and will make implementation errors easier to locate.