Go Struct Tutorial

In this Go tutorial we learn about user defined types called structs, or structures, that can store multiple variables of different types.

We learn how to define structs and their members (properties), declare struct objects and how to access struct members.

Finally, we discuss how to pass structs to functions and how to create pointers to structs.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is a struct

A struct, or structure, is a user-defined type that can store multiple variables of different types.

We use structures to model our application after entities in the real world, grouping their related code.

As an example, let’s consider a player character in an first person shooter game. Each player has a list of properties associated with them.

  • Player name
  • Hit points
  • Stamina
  • Armor
  • Primary weapon
  • Secondary weapon
  • Consumable
  • Ammunition

Our first thought would be to throw all these properties into an array, however, the properties have different types. Player name, for example, is a string and ammunition is an integer.

We could create individual variables, but what if the player wants to play a multiplayer match where more than one player’s data needs to be stored.

This is where a struct is useful. A struct allows us to group all of these properties together as a single unit, and then create multiple units.

For example, we group the player properties into a struct called Player. We can then create multiple players of type Player, each with different values.

How to define a struct

To define a struct we write the keyword type , then the name of the struct, and then the keyword struct . Lastly, we define a code block for the struct’s properties.

Syntax:
type name struct {

    // body
}

Inside the body of the struct, we can define its properties. The properties are simply variable names and their intended types.

Syntax:
type name struct {

    property_name type
}
Example:
package main

func main() {}

type Player struct {

    name string
    hitpoints int
    stamina int
}

The casing convention for structs in Go is pascal case, where the first letter of each word is uppercase.

It’s also convention to name our structs in the singular, for instance Player, not Players.

How to declare a struct object

We mentioned earlier that a struct is a custom type, like an int or a float.

To declare a struct object, we assign it to a variable with the struct name as the type, similar to a regular variable declaration.

Example:
package main

func main() {

    var p1 Player
    var p2 Player
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

In the example above, we declare two variables of type Player (the struct).

How to access struct members

To access the members (properties) of a struct, we use the name of the object, followed by a dot operator and the property name we want to access. This is known as dot notation.

Syntax:
// struct definition
type StructName struct {

    property_name type
}

// struct object
var x StructName

// dot access
x.property_name

The object will have access to all the properties inside the struct.

Example:
package main

import "fmt"

func main() {

    var p1 Player

    // the name property
    // exists inside the
    // Player struct so
    // we have access to
    // it from the object (p1)
    p1.name = "TheLegend27"
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

In the example above, we create a Player object called p1. Then we access the name property from the struct and assign a value to it.

Note that objects are independent from each other, as well as the struct itself.

If we change the value of a property in an object, it only changes in that object, not any other objects from the same struct or the struct itself.

Example:
package main

import "fmt"

func main() {

    var p1 Player
    var p2 Player

    p1.name = "TheLegend27"
    p2.name = "MorpheusCoolDude101"

    fmt.Println("p1 name:", p1.name)
    fmt.Println("p2.name:", p2.name)
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

In the example above, we assign values to the name properties of both p1 and p2. From the ouput we can see that it doesn’t overwrite, proving they are independent.

How to pass a struct to a function as an argument

If we want to pass a struct to a function, we declare it in the parameter list just like any other parameter variable.

Example:
package main

import "fmt"

func main() {

    var p1 Player

    // assign values to p1
    // properties
    p1.name = "TheLegend27"
    p1.hitpoints = 100
    p1.stamina = 80

    // pass p1 object to function
    playerLog(p1)
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

func playerLog(p Player) {

    fmt.Println("Player name:", p.name)
    fmt.Println("Player hitpoints:", p.hitpoints)
    fmt.Println("Player stamina:", p.stamina)
}

In the example above, we create a function that accepts an argument of type Player and prints its properties to the console.

In the main() function, we create a Player object called p1 and assign values to its properties. Then, we pass the object to the function that prints out the properties from p1.

If we leave a property blank, the compiler will automatically zero it out for us.

Example:
package main

import "fmt"

func main() {

    var p1 Player

    // assign values to p1
    // properties
    p1.name = "TheLegend27"
    p1.hitpoints = 100

    // pass p1 object to function
    playerLog(p1)
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

func playerLog(p Player) {

    fmt.Println("Player name:", p.name)
    fmt.Println("Player hitpoints:", p.hitpoints)
    fmt.Println("Player stamina:", p.stamina)
}

In the example above, we don’t assign a value to the stamina property. The result is that it’s initialized with a zero value, which we can see when we print it to the console.

Output:
Player name: TheLegend27
Player hitpoints: 100
Player stamina: 0

How to create a pointer to a struct

We can define pointers to structures in the same way as you define pointers to any other variable.

Example:
package main

import "fmt"

func main() {

    var p1 Player

    p1.name = "TheLegend27"

    // because the function now
    // accepts a pointer we can
    // pass the address of the
    // p1 struct
    playerLog(&p1)
}

type Player struct {

    name string
    hitpoints int
    stamina int
}

// accepts a pointer to a struct
func playerLog(p *Player) {

    fmt.Println("Player name:", p.name)
}

In the example above, we’ve changed our function to accept a pointer to a Player struct.

In the main function we use the & operator to pass the p1 struct’s address to the function as an argument.

Summary: Points to remember

  • A struct is a user defined type that can store multiple variables, called properties, of different types.
  • To define a struct we use the keyword type, then the struct name, then the keyword struct, followed by a code block.
  • In the code block we declare empty properties (variables) by writing a name and a type.
  • A struct object is an instance of the struct that holds all the properties available to the struct.
  • A struct object is declared as a variable of the struct type.
  • We access struct members through dot notation, where we specify the object name, followed by a dot operator and the property name.
  • A struct can be passed to a function as an argument if we declare a parameter as a struct type.
  • We can create a pointer to a struct in the same way we would to a normal variable.