Structs, as well as non-primitive types, are objects in Go. Go allows for object oriented programming, of a kind.
Go has no classes or inheritance, but it does provide the ability for one object to borrow behavior from another object, through the use of method set interfaces.
In the following example, cats are animals, whereas dogs only purport to be:
1package main
2import "fmt"
3
4type animal interface {
5 speak()
6 walk()
7}
8
9type cat struct {}
10type dog struct {
11 // embed the animal interface into dog
12 animal
13}
14
15func (d dog) speak() {
16 fmt.Println("woof!")
17}
18
19func (c cat) speak() {
20 fmt.Println("meow!")
21}
22
23func (c cat) walk() {
24 fmt.Println("walking!")
25}
26
27func main() {
28 c := cat{}
29 c.speak() // meow!
30 c.walk() // walking
31
32 d := dog{}
33 d.speak() // woof!
34 d.walk()
35 // panic: runtime error: invalid memory
36 // address or nil pointer dereference
37}
Because the animal interface is embedded into dog, dog’s embedded interface does have a walk() method, so the above code will compile, but it will fail with a runtime error. The animal embedded in dog has a walk() method, but because the animal interface has a nil underlying concrete object, the walk() method is called on nil, therefore the runtime panic.
Dog can, however, make c the cat the concrete object for its animal interface; c is an animal, because it implements both speak() and walk():
1package main
2import "fmt"
3
4type animal interface {
5 speak()
6 walk()
7}
8
9type cat struct {}
10type dog struct {
11 // embed the animal interface into dog
12 animal
13}
14
15func (d dog) speak() {
16 fmt.Println("woof!")
17}
18
19func (c cat) speak() {
20 fmt.Println("meow!")
21}
22
23func (c cat) walk() {
24 fmt.Println("walking!")
25}
26
27func main() {
28 c := cat{}
29 c.speak() // meow!
30 c.walk() // walking
31
32 d := dog{}
33 // set c as the underlying concrete object
34 // for d's animal
35 // This is called "borrowing".
36 // d borrows c's behavior by making c the
37 // underlying concrete object for animal
38 d.animal = c
39 // d still barks
40 d.speak() // woof!
41 // but now d can walk as well
42 d.walk() // walking!
43}
Go’s sort package implements borrowing in sort.Reverse, in a slightly different way:
1package main
2
3import (
4 "fmt"
5 "reflect"
6 "sort"
7)
8
9type person struct {
10 age int
11}
12
13type byAge []person
14
15func (b byAge) Less(i, j int) bool {
16 return b[i].age < b[j].age
17}
18
19func (b byAge) Swap(i, j int) {
20 b[i], b[j] = b[j], b[i]
21}
22
23func (b byAge) Len() int {
24 return len(b)
25}
26
27func main() {
28 b := byAge{
29 {
30 age: 11,
31 },
32 {
33 age: 33,
34 },
35 {
36 age: 2,
37 },
38 }
39 fmt.Printf("unsorted : %v\n", b)
40 sort.Sort(b)
41 fmt.Printf("sorted asc : %v\n", b)
42 // r is an struct with sort.Interface embedded in it
43
44 // type reverse struct {
45 // This embedded Interface permits Reverse to use the methods of
46 // another Interface implementation.
47 // Interface
48 // }
49
50 // The sort.Reverse function sets
51 // r.Interface's concrete object to b
52
53 // func Reverse(data Interface) Interface {
54 // return &reverse{data}
55 // }
56
57 r := sort.Reverse(b)
58
59 // r implements only the Less method, which calls
60 // the embedded Interface's Less method with its params
61 // reversed:
62
63 // func (r reverse) Less(i, j int) bool {
64 // return r.Interface.Less(j, i) // params reversed
65 // }
66
67 fmt.Printf("type of r : %v\n", reflect.TypeOf(r))
68 // Now where we call sort.Sort(r), r has its own
69 // Less method, and borrows Swap and Len from b:
70 sort.Sort(r)
71 fmt.Printf("sorted desc: %v\n", b)
72}
This is the only Go package I know of that names an interface “Interface”. This has confused more than a few engineers at first, including myself.
It’s tempting to use the words “inheritance”, and “method overriding” here. However, the Go community uses the word “borrowing”, in an effort to focus not on objects, but on their behaviors.