Embedding vs. Inheritance in Go

I'm trying to learn Go, but I keep bashing my head against some of its concepts that apply differently compared to other languages.

Suppose I have a struct

type Vehicle struct {
    Seats int
}

I now want another struct, which embeds Vehicle:

type Car struct {
    Vehicle
    Color string
}

As I understand it, the Car struct now embeds Vehicle.

Now I want to have a function that takes any vehicle

func getSeats(v Vehicle){
    return v.Seats
}

But whenever I try to pass a Car:

    getSeats(myCar)

I get the following error:
cannot use myCar (value of type Car) as Vehicle value in argument to getSeats
But my IDE tells me myCar has a Seats property!

From this I understand that embedding is not the same as inheriting.

My question is; Is there an equivalent to struct inheritance like c++, where a function can take a base struct, or is this something Go handles completely different? How would I implement something like this in the "Go way"?


Like you mentioned, Go has no inheritance in the typical sense. Embedding is really just syntactic sugar.

When embedding, you add a field to you struct with the exact same name as the type you are embedding. Any methods of the embedded struct can be called on the struct which embeds them, this does nothing more than forwarding them.

One tick is that, if the struct which embeds another already declares a method, it will be preferred over forwarding it, which allows you to sort of overwrite functions if you want to think of it like that.

As you have noticed, we can't use Car as a Vehicle even if Car embeds Vehicle since they are strictly not the same type. But any struct which embeds Vehicle will have all methods which were defined by Vehicle, thus if we define an interface which Vehicle implements, all types that embed Vehicle should also implement that interface.

For example:

package main

import (
    "fmt"
)

type Seater interface {
    Seats() int
}

type Vehicle struct {
    seats int
}

func (v *Vehicle) Seats() int {
    return v.seats
}

type Car struct {
    Vehicle
    Color string
}

type Bike struct {
    Vehicle
    Flag bool
}

// A bike always has 1 seat
func (b *Bike) Seats() int {
    return 1
}

type Motorcycle struct {
    Vehicle
    Sidecar bool
}

// A motorcycle has the base amounts of seats, +1 if it has a side car
func (m *Motorcycle) Seats() int {
    return m.Vehicle.seats + 1
}

func getSeats(v Seater) int {
    return v.Seats()
}

func main() {
    fmt.Println(getSeats(&Bike{
        Vehicle: Vehicle{
            seats: 2, // Set to 2 in the Vehicle
        },
        Flag: true,
    }))

    fmt.Println(getSeats(&Motorcycle{
        Vehicle: Vehicle{
            seats: 1,
        },
        Sidecar: true,
    }))

    fmt.Println(getSeats(&Car{
        Vehicle: Vehicle{
            seats: 4,
        },
        Color: "blue",
    }))
}

This prints:

1
2
4

In the case of Bike the Bike.Seats method is called which is why it returns 1 even when the seats value of its Vehicle is 2.

In the case of Motorcycle the Motorcycle.Seats method is also called, but here we can access the embedded type and still use it to get a result.

In the case of Car, the Vehicle.Seats method is called since Car doesn't "overwrite" Seats.


This is something I cared about when I started with Go. I thought using concepts from OOP languages like Java is natural way of doing things. Well its not, Go is not object oriented and it does not implement polymorphism trough inheritance. In the end, composition that Go supports takes good things from what inheritance offers and removes things that adds more overhead then effective help. Go's answer to polymorphism are interfaces. Go tries to be simple and you can usually achieve things only one obvious way. Lets say you have a Java classes like these:

class Base {
    private int attribute;

    public int getAttribute() {
        return this.attribute;
    }
}

class Something extends Base {}

In java every object is on the heap and you have pointer to it. It can be null and it also means it has same size when you pass it around. This is one of the reasons why you can pass objects more freely (Whether you pass Base or Something, as long as argument type is super class, it will compile). Go is designed to manage memory more efficiently and use stack more then heap, and even heap is less fragmented. When you declare a struct, the size of that struct is determined by data it holds, so you cannot pass it to places where embedded structure belongs. Let's give an example:

type Base struct {
   attribute int32 
}

func (b *Base) Attribute() int32 {
    return b.attribute
}

type Something struct {
    garbage int32
    Base
}

While Base has 4 bytes, Something has 8, and attribute has different offset. If the compiler lets you pass Something in place of Base, you would access garbage instead of attribute. If you want to get this behavior you have to use interface. Luckily, all you have to declare is this:

type BaseInterface interface {
    Attribute() int32
}

Now that you have this interface, you can make general functions over all structs that embed Base (unless it also embeds something else that has method names Attribute on the same level) like this:

func DoubleOfBaseAttribute(base BaseInterface) int32 {
    return base.Attribute() * 2
}

This is also called Dynamic Dispatch and its what languages like C++ or Java use implicitly.