Inheritance and Composition in GO

Structs can “inherit” from other structs by a method known as embeding. Here is a simple example:

package main

import (
    "fmt"
)

type a struct {
    Name string
}
//embeds a value of type a
type b struct {
    a
}
//embeds a pointer to an a
type c struct {
    *a
}

func main() {
    a := a{Name: "Janeway"}
    fmt.Println(a.Name)
    b := &b{a: a}
    fmt.Println(b.Name)
    c := &c{a: &a}
    fmt.Println(c.Name)
}

playground

If the struct that is embeded has methods then the struct into which it is embedded will also have those methods

package main

import (
    "fmt"
)

type a struct {
    Name string
}

func (as a) NameLength() int {
    return len(as.Name)
}

type b struct {
    a
}

type c struct {
    *a
}

func main() {
    a := a{Name: "Janeway"}
    fmt.Println(a.Name)
    fmt.Println(a.NameLength())
  
    b := &b{a: a}
    fmt.Println(b.Name)
    fmt.Println(b.NameLength())
  
    c := &c{a: &a}
    fmt.Println(c.Name)
    fmt.Println(c.NameLength())
}

So what happens if b also has a method called NameLength?

package main

import (
    "fmt"
)

type a struct{
    Name string
}

func (as a)NameLength() int{
    return len(as.Name)
}

type b struct{
 a
}

func (bs b)NameLength() int{
    return len(bs.Name) -1
}

type c struct{
*a
}

func main() {
    a := a{Name:"Janeway"}
    fmt.Println(a.Name)
    fmt.Println(a.NameLength())
  
    b := &b{a:a}
    fmt.Println(b.Name)
    fmt.Println(b.NameLength()) //this is the method on b itself
    fmt.Println(b.a.NameLength()) //notice we can still reference the behaviour of a
  
    c := &c{a:&a}
    fmt.Println(c.Name)
    fmt.Println(c.NameLength())
}
//will output
//Janeway
//7
//Janeway
//6
//7
//Janeway
//7

playground

So it acts as I think we would expect it to. If there is a method on the referenced struct that matches it will chose that implementation, however if you want to call the method on the embedded struct you can do so by going up the chain of properties

b.a.MethodName()

So what’s the difference between this and classical inheritance?

  1. The embedded struct knows nothing about the struct in which it has been embedded. There is no way to go down the chain you cannot do a.b.MethodName
  2. There is no way to have the concept of abstract methods that the embedding struct is forced to implement.

Again the rules around pointers apply. If you modify an embeded pointer you will also modify the value of that pointer for anything else using the same pointer address.


See also