Golang - Chapter 13

Structs

Go has structs instead of classes as we see in other languages. Structs basically encompass some fields within.

package main

import (
	"fmt"
)

type School struct {
    Name string
    Country string
    NumOfStudents int
}

func main() {
    school := School{"ABC Primary School", "Canada", 5000}
	fmt.Println(school.Name)
}

Above, we have school as a struct that contains Name, Address and the NumOfStudents.

To access any of the fields within a struct, we use the . operator, e.g. school.Name.

We can initialise a struct in a number of ways.

func main() {
	// initializing according to the order of fields without specifying field names
    school := School{"ABC Primary School", "Canada", 5000}
	fmt.Println(school.Name)
	
	// initializing with field names and in random order
	school2 := School{Name: "XYZ Primary School", NumOfStudents: 2000, Country: "America"}
	fmt.Println(school2.NumOfStudents)
	
	// initializing only some struct fields
	school3 := School{Name:"Jingle Bells Secondary School"}
	fmt.Println(school3.Name)
}

The above will output the following:

ABC Primary School
2000
Jingle Bells Secondary School

Note

Naming the fields within the struct starting with capital letters allows us to access them freely. Read on to find out how to restrict access.

Anonymous Struct Members

In a way, inheritance can be done using anonymous struct members.

type Lab struct {
    numOfEquipment int 
}
 
type School struct {
    Lab //anonymous field
    numOfStudents int 
}

func main() {
	// initialise a school with 300 students and a lab with 10 equipment 
    school := School{Lab{10}, 300}
	fmt.Println(school.numOfEquipment)
}

We are achieving inheritance via composition. This means school.numOfEquipment is a valid access even though School does not directly contain this field.

If two structs which you are "inserting" from has the same property name, we need to specify the "intermediate" struct type and then the property name.

type Lab struct {
    numOfEquipment int
}

type SportsRoom struct {
    numOfEquipment int
}

type School struct {
    Lab
    SportsRoom
}

func main() {
    school := School{Lab{3}, SportsRoom{4} }
    fmt.Println(school.Lab.numOfEquipment + school.SportsRoom.numOfEquipment)
}

Methods on Structs

We can have pointer receivers for structs, i.e. the receiver type has a syntax *T, where T is the type of struct.

package main

import (
	"fmt"
)

type Circle struct {
    radius int
}

// pointer receiver method
func increaseRadiusByOne(c *Circle) {
    c.radius += 1
}

// value receiver method
func increaseRadiusByTwo(c Circle) {
    c.radius += 2
}

func main() {
    c := Circle{2}
    increaseRadiusByOne(&c)	// passing the reference to c
    fmt.Println(c.radius)	// prints 3
    
    increaseRadiusByTwo(c)	// passing a copy of c
    fmt.Println(c.radius)	// still prints 3
}

Using a pointer receiver will modify the original struct as it receives a reference to the actual object itself. In the above example, increaseRadiusByOne(&c) passes the actual object c defined in the main method. Therefore, the actual radius is modified.

Using a value receiver receives a copy of the struct. Modifying the struct properties within this, there will not be any modifications on the original struct. In the above example, increaseRadiusByTwo(c) passes a copy of c and modifying the copy of c does not affect the actual c defined in the main method.

comments powered by Disqus

Related