Extending a Struct in Go

November 21st 2018

In object-oriented programming, we can extend classes in order to re-use a lot of existing code.

For this extension, Go doesn't allow us to inherit from the types - instead we're extending the existing struct via composition.

This allows us to use all of the same properties and functions of the original struct.

Want to read more posts like this? Subscribe to the blog! Sign in with Github

I've found this to be especially useful when encoding and decoding JSON. As the standard library encoding/json correctly decodes into the extended struct.

package main

import (
    "fmt"
)

type Greeting struct {
    FirstName string
}

func (g Greeting) GetName() string {
    return g.FirstName
}

func (g Greeting) OldPrint() {
    fmt.Printf("Hello, %s\n", g.GetName())
}

// --- Extend Greeting Struct ---
type ExtendedGreeting struct {
    Greeting
    LastName string
}

// ---

func (ext ExtendedGreeting) GetName() string {
    return fmt.Sprintf("%s %s", ext.FirstName, ext.LastName)
}

func (ext ExtendedGreeting) NewPrint() {
    fmt.Printf("Hello, %s\n", ext.GetName())
}

func main() {
    greeting := Greeting{FirstName: "John"}
    extended := ExtendedGreeting{Greeting: greeting, LastName: "Snow"}

    extended.OldPrint()             // "Hello, John"
    extended.NewPrint()             // "Hello, John Snow"
    fmt.Println(extended.FirstName) // "John"
}

Gotchas

One thing to note is that functions that expect the old struct will bypass any overridden functions that you have written. Take a look at how extended.OldPrint() still calls Greeting#GetName() even though ExtendedGreeting#GetName() exists.

Want to read more posts like this? Subscribe to the blog! Sign in with Github