Shine on You Crazy Interface - part1


Go’s interfaces are brilliant. It’s not immediately apparent and at first can be confusing, but after a while its brilliance shines through.

Simply understand that if a concrete type posses all necessary methods it, the concrete type, can implicitly satisfy an interface.

In Go, interfaces are considered abstract types, meaning we do not know what an interface really is, only what it can do.

The Go standard library makes use of interfaces, let’s take a look at an example. Say we have the following:

Full example can be found here: https://play.golang.org/p/f9NkR_oSMo1

type song struct {
    name    string
    release time.Time
}

type songs []song

func (s songs) String() string {
    var ss []string
    for i := range s {
        ss = append(ss, s[i].name+" "+fmt.Sprint(s[i].release.Year()))
    }
    return strings.Join(ss, ", ")
}

s := songs{
    {"Comfortably Numb", time.Date(1979, time.November, 0, 0, 0, 0, 0, time.UTC)},
    {"Interstellar Overdrive", time.Date(1967, time.August, 0, 0, 0, 0, 0, time.UTC)},
    {"Time", time.Date(1973, time.March, 0, 0, 0, 0, 0, time.UTC)},
    {"High Hopes", time.Date(1994, time.March, 0, 0, 0, 0, 0, time.UTC)},
}

fmt.Println(s)
// Comfortably Numb 1979, Interstellar Overdrive 1967, Time 1973, High Hopes 1994

Say we want to sort the above based on release date so we can listen to Pink Floyd in chronological order. We could write our own sorting function, but instead, let’s see if we can implement the sort.Sort from the standard library:

func Sort(data Interface)
// where Interface is an abstract type that has three methods
type Interface interface {
    // comments omitted for brevity
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

So to implement sort.Interface we write the following three methods for our songs type:

func (s songs) Len() int {
    return len(s)
}

func (s songs) Less(i, j int) bool {
    return s[i].release.Before(s[j].release)
}

func (s songs) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

The sort.Sort function doesn’t care about the underlying type. So long as the type has behavior through its methods that implements Len, Less and Swap it can be sorted… (usually)

sort.Sort(s)
fmt.Println(s)
// Interstellar Overdrive 1967, Time 1973, Comfortably Numb 1979, High Hopes 1994

Note as of Go 1.8 it’s even easier to implement slice sort. See go1.8#sort_slice

Full example can be found here: https://play.golang.org/p/8b2OokpqsLA

// to implement sort.Slice all we need is the Less method from above, omit Len and Swap entirely
// func Slice(slice interface{}, less func(i, j int) bool)

sort.Slice(s, s.Less)
fmt.Println(s)
// Interstellar Overdrive 1967, Time 1973, Comfortably Numb 1979, High Hopes 1994

To wrap things up, we were able to leverage a standard library sorting function through an interface. Our concrete type songs implicitly implemented the sort.Sort function because it had the necessary behavior (methods). The interface type sort.Interface defined the contract between sort.Sort and its caller. Awesome!