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.
getSeats(myCar.Vehicle), or make Vehicle an interface if you want polymorphism.Now I want to have a function that takes any vehicleDo you really need to to begin with. The example is contrived and I find it hard to perceive the need here.