Structs

3-minute read
Table of Contents

These are typed collections of fields. We use structs in order to group related data together as records. Consider a struct with four ($4$) member variables:

package main

import "fmt"

type person struct {
	name      string
	age       int
	xPosition int
	yPosition int
}

func main() {
	// declaration by member variable order
	p := person{"John", 32, 0, 0}
	fmt.Println("person:", p)

	fmt.Println("name:", p.name)
	fmt.Println("age:", p.age)
	fmt.Println("x position:", p.xPosition)
	fmt.Println("y position:", p.yPosition)

	// declaration with named member variables
	p2 := person{name: "Mary", age: 5, xPosition: 2, yPosition: 9}
	fmt.Println("person 2:", p2)

	fmt.Println("name:", p2.name)
	fmt.Println("age:", p2.age)
	fmt.Println("x position:", p2.xPosition)
	fmt.Println("y position:", p2.yPosition)
}
struct-person.go
Copy

For person p, we list the member variables in the order in which they are declared in the struct - name, age, xPosition, yPosition respectively. For person p2, we use the names of the member variables - these can be listed in any order. The latter form is more commonly used as the former is not very readable unless you know what the original struct declaration looks like and the specific order of the member variables/fields:


type person struct {
	name      string
	age       int
	xPosition int
	yPosition int
}

Automatic zero-value

When we omit any field, it will be zero-valued automatically:

package main

import "fmt"

type person struct {
	name      string
	age       int
	xPosition int
	yPosition int
}

func main() {
	p := person{name: "John", age: 32}

	fmt.Println("person:", p)

	fmt.Println("name:", p.name)
	fmt.Println("age:", p.age)
	fmt.Println("x position:", p.xPosition)
	fmt.Println("y position:", p.yPosition)
}
struct-person-zero-valued.go
Copy

xPosition and yPosition will be zero-valued as $0$. This is because they are integers and integers in Go have a zero-value of $0$.

Automatic dereferencing

If we use a struct pointer with the dot notation, the pointer is automatically dereferenced, saving us having to use the unary operator *:

package main

import "fmt"

type person struct {
	name      string
	age       int
	xPosition int
	yPosition int
}

func main() {
	p := &person{
		name:      "James",
		age:       13,
		xPosition: 0,
		yPosition: 3,
	}

	fmt.Println("name:", p.name)
	fmt.Println("age:", p.age)
	fmt.Println("x position:", p.xPosition)
	fmt.Println("y position:", p.yPosition)
}
automatic-struct-pointer-dereference.go
Copy

Constructors and garbage collection

Because Go is garbage collected, we can return a pointer to a variable local to the constructor function and this value will be cleaned when there are no active references to it:

package main

import "fmt"

type person struct {
	name      string
	age       int
	xPosition int
	yPosition int
}

func newPerson(myName string) *person {
	p := person{name: myName}
	p.age = 5
	return &p
}

func main() {
	p := newPerson("John")

	fmt.Println("name:", p.name)
	fmt.Println("age:", p.age)
	fmt.Println("x position:", p.xPosition)
	fmt.Println("y position:", p.yPosition)

}
struct-garbage-collected.go
Copy

Anonymous structs

For table-driven tests and one-off uses of structs, where we do not care about using the name of the struct in future, we can use anonymous struct type:

package main

import "fmt"

func main() {
	dog := struct {
		name   string
		isGood bool
	}{
		"Rex",
		true,
	}
	fmt.Println("dog:", dog)
	fmt.Println("dog name:", dog.name)
	fmt.Println("dog is good:", dog.isGood)
}
struct-anonymous.go
Copy

Support us via BuyMeACoffee