Skip to main content

Command Palette

Search for a command to run...

Interface compliance at compile-time in Go

How to be sure that your struct implements the interface

Updated
3 min read
Interface compliance at compile-time in Go
M

Software Engineer x Data Engineer - I make the world a better place to live with software that enables data-driven decision-making

Interfaces

Imagine having an interface called Runner that looks like this:

type Runner interface {
    Run() error
}

And a struct called Worker that you want to implement the mentioned interface. What you need to do? Ofc the only thing required is to add a Run function according to the interface:

type Worker struct {
}

func (w Worker) Run() error {
    return nil
}

Voilà! Now your Worker struct implements the Runner interface.

The whole program now looks like this:

package main

import "fmt"

type Runner interface {
    Run() error
}

type Worker struct {
}

func (w Worker) Run() error {
    return nil
}

func main() {
    w := Worker{}

    fmt.Println("worker", w)
}

You can run your program and everything is fine:

➜  interfaces git:(main) ✗ go run main.go
worker {}

Interface Mutation → Problem

Okay, but what will happen if you change the Runner interface?

type Runner interface {
    Run() error
    Stop() error
}

Well, you run the program and… nothing happens. Program runs successfully, but Worker is no longer compliant with the Runner interface, bc it’s missing the Stop function:

➜  interfaces git:(main) ✗ go run main.go
worker {}

Compile-time Compliance

How to be sure that your struct implements the interface at a complie-time? That’s easy! You just need to add this line:

var _ Runner = (*Worker)(nil)

What it does, you ask?

  • The underscore _ is used only as a blank identifier, means to ignore the value.

  • The Runner is the interface that we want to check.

  • And the right side of the assignement is a type conversion of nil to a pointer of the Worker struct.

Thanks to this we enforce at the compile-time that the given type (Worker) implements the interface (Runner).

Having the main packages like this (with the missing Stop function in the Worker):

package main

import "fmt"

type Runner interface {
    Run() error
    Stop() error
}

var _ Runner = (*Worker)(nil)

type Worker struct {
}

func (w Worker) Run() error {
    return nil
}

func main() {
    w := Worker{}

    fmt.Println("worker", w)
}

When we try to run this program:

➜  interfaces git:(main) ✗ go run main.go
# command-line-arguments
./main.go:10:16: cannot use (*Worker)(nil) (value of type *Worker) as Runner value in variable declaration: *Worker does not implement Runner (missing method Stop)

We got a compile-time error.

Let’s fix this quickly by adding a missing Stop function:

func (w Worker) Stop() error {
    return nil
}

And we’re good:

➜  kiss-samples git:(main) ✗ go run main.go
worker {}

That’s it! Hope you like it and use it in your own projects!

Sources