Ensuring Go interface satisfaction at compile-time

In my last post, I described a technique to ensure that a test implementation satisfies an interface, without having to implement every method of the interface. If you haven’t read it, do that now, I’ll wait.

That technique is great for tests. Your test implementations only have to stub out the methods you care about for that particular test scenario, and they’ll continue to work if the interface changes in ways unrelated to your test.

But, as the post mentions, it’s not suitable for production code. If an unimplemented method does manage to get called, you get a nasty panic.

There’s another technique for ensuring that your production implementations satisfy their interfaces at compile-time:

type Interface interface {
Foo()
}
type Implementation struct {}
func (*Implementation) Foo() { fmt.Println("foo!") }
var _ Interface = (*Implementation)(nil) // <---

This last line will ensure that your Implementation satisfies your Interface, and will fail to compile if Interface adds methods that the Implementation fails to satisfy.

Let’s dig into that line a little more. It says, define a global unnamed var (“_”) of type Interface, whose value is a nil pointer, cast as a *Implementation.

If that nil pointer cast as a *Implementation doesn’t satisfy Interface, the var is invalid, and you’ll get a compiler error:

prog.go:16 cannot use (*Implementation)(nil) (type (Implementation) as type Interface in assignment:
*Implementation does not implement Interface (missing Foo method)

Try it out in the Playground: http://play.golang.org/p/yxdU-F1tWm

And, because it’s just an unnamed global var, it will be compiled out after it passes the type check, so it won’t have any impact at all on runtime.

Two approaches to changing interfaces

So now we’ve got two complimentary techniques for dealing with changing interfaces.

  1. Embed the Interface inside the Implementation, ensuring the Implementation satisfies the Interface, but only vacuously — unimplemented methods will panic, making this a useful hack for tests, where you don’t care about methods you don’t call.
  2. Declare a var stating that Implementation satisfies the Interface, ensuring that your code will fail to compile if it doesn’t satisfy. This is useful for production code, where you’ll fail to compile without satisfaction.

Each have their ups and downs, and each are useful in their own ways. But each are very useful when you find yourself dealing with interfaces outside your control.

And even if you do control the interface, declaring definitively that an implementation must satisfy the interface can be a nice piece of compiler-enforced documentation for your teammates.