Interface Satisfaction

In Go+, interfaces do not need to be explicitly implemented – i.e. no implement keyword. Instead, interfaces are satisfied implicitly.

A type satisfies an interface if it possesses all the methods the interface requires. For example, an *os.File satisfies io.Reader, io.Writer, io.Closer, and io.ReadWriter. A *bytes.Buffer satisfies io.Reader, io.Writer, and io.ReadWriter, but does not satisfy io.Closer because it does not have a Close method.

The assignability rule for interfaces is very simple: an expression may be assigned to an interface only if its type satisfies the interface. This rule applies even when the right-hand side is itself an interface. For example:

import ( "bytes" "io" "os" "time" ) var w io.Writer w = os.Stdout // OK: *os.File has Write method w = new(bytes.Buffer) // OK: *bytes.Buffer has Write method w = time.Second // compile error: time.Duration lacks Write method var rwc io.ReadWriteCloser rwc = os.Stdout // OK: *os.File has Read, Write, Close methods rwc = new(bytes.Buffer) // compile error: *bytes.Buffer lacks Close method w = rwc // OK: io.ReadWriteCloser interface has Write method rwc = w // compile error: io.Writer interface lacks Close method

Because io.ReadWriter and io.ReadWriteCloser include all the methods of io.Writer, any type that satisfies io.ReadWriter or io.ReadWriteCloser necessarily satisfies io.Writer.

Like an envelope that wraps and conceals the letter it holds, an interface wraps and conceals the concrete type and value that it holds. Only the methods revealed by the interface type may be called, even if the concrete type has others. For example:

import ( "io" "os" ) os.Stdout.Write([]byte("hello")) // OK: *os.File has Write method os.Stdout.Close() // OK: *os.File has Close method var w io.Writer w = os.Stdout w.Write([]byte("hello")) // OK: io.Writer has Write method w.Close() // compile error: io.Writer lacks Close method

A concrete type may satisfy many unrelated interfaces. Consider a program that organizes or sells digitized cultural artifacts like music, films, and books. It might define the following set of concrete types:


We can express each abstraction of interest as an interface. Some properties are common to all artifacts, such as a title, a creation date, and a list of creators (author s or artists).

import "time" type Artifact interface { Title() string Creators() []string Created() time.Time }

Other properties are restricted to certain types of artifacts. Properties of the printed word are relevant only to books and magazines, where as only movies and TV episodes have a screen resolution.

import ( "io" "time" ) type Text interface { Pages() int Words() int PageSize() int } type Audio interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string // e.g., "MP3", "WAV" } type Video interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string // e.g., "MP4", "WMV" Resolution() (x, y int) }

These interfaces are but one useful way to group related concrete types together and express the facets they share in common. We may discover other groupings later. For example, if we find we need to handle Audio and Video items in the same way, we can define a Streamer interface to represent their common asects without changing any existing type declarations.

import ( "io" "time" ) type Streamer interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string }

Each grouping of concrete types based on their shared behaviors can be expressed as an interface type. Unlike class-based languages, in which the set of interfaces satisfied by a class is explicit, in Go we can define new abstractions or groupings of interest when we need them, without modifying the declaration of the concrete type. This is particularly useful when the concrete type comes from a package written by a different author. Of course, there do need to be underlying commonalities in the concrete types.

Next example: Interface Values