What's a struct?

A struct in Golang is a collection of fields

Structs allow you to:

  • Control data types
  • Create complex data objects
  • Know what types to expect in returned objects
  • Allows for natural Composition or the "putting together" of programmatically related types

The basic struct definition is as follows:


type Name struct {
Field type
Field2 type
}

Below we have two structs... money and credit


type Money struct {
Dollars int
Cents int
}

type Credit struct {
Money *Money
}

Money is made up of two integers. Dollars and cents. Credit is made up of Money which is a memory pointer to the Money struct.

The example below demonstrates how to populate an example of each struct and their relationship.


package main

import "fmt"

type Money struct {
Dollars int
Cents int
}

type Credit struct {
Money *Money
}

func main() {
money := &Money{
Dollars: 10,
Cents: 04,
}

credit := &Credit{
Money: money,
}

fmt.Println("Pointer: ",&credit.Money)
fmt.Println("Value: ",credit.Money)
fmt.Println("Dollars: ",credit.Money.Dollars)
fmt.Println("Cents: ",credit.Money.Cents)
}

Notice how the Money struct literal that is saved in the money variable is set as the Money field of the Credit struct literal, which is saved in the credit variable.

We can easily access the memory pointer of `credit.Money` and the actual value and the dollars and cents

Structs help with "inheritance" in Go

Golang doesn't support traditional OOP inheritance. It does support compositon however. You can use it with structs in the three following ways:

  • Embedding the struct as a pointer
  • Embedding the struct as a value
  • Inherit from an interface as first value of a struct, unnamed

Let's say we just wanted the Credit struct to "inherit" the Money Struct and all it properties and methods. We could do something like this:


type Money struct {
Dollars int
Cents int
}

type Credit struct {
*Money
}

Notice that the Money field in the Credit struct is an unnamed pointer. If the field wasn't a pointer and we wanted to "inherit" from the value we would do this:


type Money struct {
Dollars int
Cents int
}

type Credit struct {
Money
}

In both these examples the "Money" field is unnamed in the Credit struct definition, but actually does have a name. It assigns the name Money from the imbedded type definition (the name of the embedded struct.)

The third example where a struct "inherits" from an interface is much more complicated. Lets say that our money definition was an interface instead of a struct:


type Money interface {
Dollars() int
Cents() int
}

The Credit struct definition can "inherit" all the methods and fields from the Money interface like so:


type Credit struct {
Money
}

Below is an implementation demonstrating how this would work:


package main

import (
"fmt"
)

type Money interface {
Dollars() int
Cents() int
}

type Savings struct {
dollars int
cents int
}

type Credit struct {
Money
}

func (s Savings) Dollars() int {
return s.dollars
}

func (s Savings) Cents() int {
return s.cents
}

func main() {
credit := Credit{
&Savings{
dollars: 100, 
cents: 50,
},
}

fmt.Println(credit.Dollars(),".",credit.Cents())
}

As you can see, since Savings implements the Money interface, when we set a Savings struct literal as the value of the unnamed Money field of the Credit type, the Credit type can access all the methods of the Savings type.

 

Conclusion

Structs are an integral part of the Golang language. They grant the ability to define very specific and complex data types and allow through composition the ability to share methods and fields. They give a very clear transparency of what types of data to expect when data is created, processed and returned