Tuesday, March 05, 2013

A simple illustration of Go interfaces

Go's approach to object orientation is somewhat different to the familiar 'hierarchy of classes'. In place of classes with methods, Go has interfaces. An interface specifies a set of behaviors (functions that can be called). If a Go type has that interface (i.e. has all the functions specified in the interface) then it can be used wherever that interface is required.

A commonly used interface in Go is io.Writer. An io.Writer interface is defined as follows in the io package:
type Writer interface {
    Write(p []byte) (n int, err error)
}
Any user-defined type that has a function with signature Write(p []byte) (n int, err error) is an io.Writer and can be used wherever an io.Writer is needed. For example, the bufio package can be used to create a buffered version of any io.Writer by passing it to bufio.NewWriter.
func NewWriter(wr io.Writer) *Writer
bufio.NewWriter's argument is itself an interface (an io.Writer), so anything that has the Write(p []byte) (n int, err error) function can be passed to it. At the same time bufio.Writer (the type returned by bufio.NewWriter) is itself an io.Writer (because it too has the Write(p []byte) (n int, err error) function.

This style makes it possible to define other io.Writers that can be used with any type requiring an io.Writer. In one project I was working on it was helpful to have an io.Writer that kept count of how many bytes were written. Here's that package (called rather unimaginatively counter).

With that package any existing io.Writer could be wrapped with a call to counter.New() and the return result used as if it were the original io.Writer.

1 comment:

Evan Jones said...

It may be worth pointing out that your specific example of chaining Writers could also be implemented in "traditional" OO languages C++ and in Java.

The part of Go interfaces that I love is that structs do not specifically declare that they implement the interface. You can pass in any struct, and if it has the right methods, Go makes it work. This example doesn't actually use that fact.