skip to content
Alvin Lucillo

Grouping types in Go

/ 2 min read

💻 Tech

Go doesn’t have a context of subtypes that if you embed a struct into a struct, the latter will become a subtype of the former.

The example below is a bad design and has syntax error.

type Animal struct {
	Nome     string
    Idade    int
}
type Cachorro struct {
	Animal
}
type Gato struct {
	Animal
}

func (c *Cachorro) Fala() {
	fmt.Printf("Woof!")
}

func (g *Gato) Fala() {
	fmt.Printf("Meow!")
}

// ...

func main() {
        // syntax error
    	animais := []Animal{
            Cachorro: Animal{
                Nome: "Pluto",
                Idade: 1,
            },
            Gato: Animal{
                Nome: "Tom",
                Idade: 2,
            },
        }

        for _, animal := range animais {
		    animal.Fala()
	    }   
}

The above code is a bad design because it’s creating Animal struct that’s only embedded to maintain state. Furthermore, it assumes subtyping, that’s why it’s a syntax error in the slice of Animal part.

A rule of thumb in grouping types is determining what it does or the behavior. In the example above, they all speak, so we can create an interface.

type Acao interface {
	Fala()
}

We then eliminate the use of Animal struct. Each animal can maintain their own state. After creating objects from those animals, we can then use the interface to group the types.

type Cachorro struct {
	Nome     string
    Idade    int
}
type Gato struct {
	Nome     string
    Idade    int
}

func (c *Cachorro) Fala() {
	fmt.Printf("Woof!")
}

func (g *Gato) Fala() {
	fmt.Printf("Meow!")
}

// ...

func main() {
    	animais := []Acao{
            Cachorro: Animal{
                Nome: "Pluto",
                Idade: 1,
            },
            Gato: Animal{
                Nome: "Tom",
                Idade: 2,
            },
        }

        for _, animal := range animais {
		    animal.Fala()
	    }   
}

In summary, we group the types based on what they do (i.e., behavior). One basic principle in Go is not to design interface upfront but to create them as they become necessary.