Golang Structs

Using Structs in Golang

Golang provides built-in types such as bool, byte, string as well as the ability to define custom types. Users create a custom type by defining a collection of fields known as a struct. Each field has an explicitly defined type; the type can be a built-in type or a user defined type. Because they can be used to store complex information records and accommodate associated method functions, structs are considered the go equivalent of classes used in traditional object oriented programming.

Creating Structs

Empty Structs

You can create an empty struct instance by not specifying any fields when instantiating. Doing so sets each struct field to the default value for its type (e.g. an empty string for string, 0 for float64, etc.):

type Planet struct {
    class    string
    diameter float64
    name     string
}

func main() {
    var planet1 Planet
    planet2 := Planet{}

    fmt.Println("planet1:", planet1)
    fmt.Println("planet2:", planet2)
}
planet1: { 0 }
planet2: { 0 }


You can also use pointers to create the usual empty struct instance or a nil struct instance:

type Planet struct {
    class    string
    diameter float64
    name     string
}

func main() {
    var planet1 *Planet
    planet2 := &Planet{}
    planet3 := new(Planet)

    fmt.Println("planet1:", planet1)
    fmt.Println("planet2:", planet2)
    fmt.Println("planet3:", planet3)
}
planet1: <nil>
planet2: &{ 0 }
planet3: &{ 0 }

Struct Literals

Struct instances aren’t very helpful if they’re empty, so we define struct fields when instantiating with struct literals:

type Planet struct {
    class    string
    diameter float64
    name     string
}

func main() {
    // instantiate with field keys and values
    planet1 := Planet{
        class: "M",
        diameter: 12742,
        name:  "Earth",
    }

    // instantiate with field values
    planet2 := Planet{"K", 6779, "Mars"}

    // instantiate pointer with field keys and values
    planet3 := &Planet{
        class: "J",
        diameter: 116460,
        name: "Saturn",
    }

    fmt.Println("planet1:", planet1)
    fmt.Println("planet2:", planet2)
    fmt.Println("planet3:", planet3)
}
planet1: {M 12742 Earth}
planet2: {K 6779 Mars}
planet3: &{J 116460 Saturn}

Nested Structs

In addition to the built-in types, you can also define a struct field as a user defined type (i.e. another struct):

type Class struct {
    description string
    name        string
}

type Planet struct {
    class    Class
    diameter float64
    name     string
}

func main() {
    planet := Planet{
        class: Class{
            description: "Earth-like atmosphere containing oxygen - habitable for humanoid life forms",
            name:        "M",
        },
        diameter: 12742,
        name:     "Earth",
    }

    fmt.Println("planet:", planet)
}
planet: {{Earth-like atmosphere containing oxygen - habitable for humanoid life forms M} 12742 Earth}

Accessing Structs

Struct fields can be accessed with dot notation. If the struct instance is a pointer, golang de-references the pointer automatically and accesses the struct fields:

type Class struct {
    description string
    name        string
}

type Planet struct {
    class    Class
    diameter float64
    name     string
}

func main() {
    classM := Class{
        description: "Earth-like atmosphere containing oxygen - habitable for humanoid life forms",
        name:        "M",
    }

    classJ := Class{
        description: "Gas giant",
        name:        "J",
    }

    planet1 := Planet{
        class: classM,
        diameter: 12742,
        name:  "Earth",
    }

    planet2 := &Planet{
        class: classJ,
        diameter: 116460,
        name: "Saturn",
    }

    fmt.Println("planet:")
    fmt.Println("    name:", planet1.name)
    fmt.Println("    class:", planet1.class.name)
    fmt.Println("    diameter:", planet1.diameter, "km")

    // planet2 is a pointer to a struct instance that is
    // automatically dereferenced and its fields accessed for printing
    fmt.Println("planet:")
    fmt.Println("    name:", planet2.name)
    fmt.Println("    class:", planet2.class.name)
    fmt.Println("    diameter:", planet2.diameter, "km")
}
planet:
    name: Earth
    class: M
    diameter: 12742 km
planet:
    name: Saturn
    class: J
    diameter: 116460 km

Modifying Structs

All struct instances are mutable so they can be modified after they have been instantiated:

type Planet struct {
    class    string
    diameter float64
    name     string
}

func main() {
    var planet Planet

    planet.class = "N"
    planet.diameter = 12104
    planet.name = "Venus"

    fmt.Println("planet:", planet)

    planet.class = "J"
    planet.diameter = 139820
    planet.name = "Jupiter"

    fmt.Println("planet:", planet)
}
planet: {N 12104 Venus}
planet: {J 139820 Jupiter}

Struct Methods

You can define a method for a struct by specifying the struct as a receiver to a function:

type Planet struct {
    class    string
    diameter float64
    name     string
}

func (p Planet) getCircumference() float64 {
    return math.Pi * p.diameter
}

func main() {
    planet := Planet{
        class: "J",
        diameter: 49244,
        name: "Neptune",
    }

    fmt.Println("planet:")
    fmt.Println("    name:", planet.name)
    fmt.Println("    class:", planet.class)
    fmt.Println("    circumference:", planet.getCircumference(), "km")
    fmt.Println("    diameter:", planet.diameter, "km")
}
planet:
    name: Neptune
    class: J
    circumference: 154704.58863337577 km
    diameter: 49244 km