Maps in Go
Learn how Maps work in Go.
The Go programming language is not an object-orientated language. There are no Class objects, or inheritance, that you typically find in other OOP languages like Python, Java, C#, and others. However, there is an alternative data type in Go called struct, short for structure, that provides us with comparable features.
In this post, we introduce the struct data type in Go and how to create and use them.
Creating a struct is done with the type <struct name> struct {}
syntax.
In the example below, we create an IceCream struct with field values for Flavor, Description, Price, and Vegan. After each field, we specify the field type.
package main
import "fmt"
type IceCream struct {
Flavor string
Description string
Price float64
Vegan bool
}
func main() {
icecream := IceCream{
Flavor: "Vanilla",
Description: "Plain tasting ice cream",
Price: 5.99,
Vegan: false,
}
// print the struct
fmt.Println(icecream)
// print a single field value
fmt.Println(icecream.Flavor)
}
The output is:
{Vanilla Plain tasting ice cream 5.99 false}
Vanilla
The Go compiler lets you create structs without using the field names too. The code snippet below creates the same struct but without the use of field names.
icecream := IceCream{
"Vanilla",
"Plain tasting ice cream",
5.99,
false,
}
However, this reduces clarity and could make it more difficult to create maintainable code in larger production software. Many Go programmers prefer the explicit declaration method of <field name>: <field value>
.
Structs are mutable. We can assign new field values to a struct after it’s already been created.
icecream.Price = 10.99
icecream.Price += 1
When you create a struct without specifying all the field values, the zero-value gets assigned to those fields.
For example, the code snippet below creates an icecream struct with only the Flavor and Vegan fields defined. As a result, the Price field is set to 0
and the Description field is set to an empty string.
icecream := IceCream{
Flavor: "Vanilla",
Vegan: false,
}
// print the price
fmt.Println(icecream.Price)
// print the description
fmt.Println(icecream.Description)
The output is:
0
Go provides several printing “verbs” that can be used to format the output of printed values. These can be found in the Go documentation for the fmt package.
Update the previous example with a format print statement. This will print both the field names and field values.
fmt.Printf("%+v\n", icecream)
The output is:
{Flavor:Vanilla Description:Plain tasting ice cream Price:10.99 Vegan:false}
Structs can be associated, or nested, with other structs. One struct might also contain a slice of other structs. This is something we’ll try next.
Let’s create a new struct that contains a list of IceCream structs. This is a common way to collect and organize data.
Below is a basic example on how to achieve this in Go.
package main
import "fmt"
type IceCream struct {
Flavor string
Price float64
Quantity int
}
type Inventory struct {
Items []IceCream
}
func main() {
inventory := Inventory{}
icecream := IceCream {
Flavor: "Vanilla",
Price: 5.99,
Quantity: 10,
}
inventory.Items = append(inventory.Items, icecream)
icecream2 := IceCream {
Flavor: "Chocolate",
Price: 7.99,
Quantity: 12,
}
inventory.Items = append(inventory.Items, icecream2)
// print the inventory
fmt.Println(inventory)
}
An IceCream
struct is defined with three fields representing the Flavor, Price, and Quantity. We then create an Inventory struct with a single field Items
that contains a slice of IceCream
structs.
In the main()
function, we create a variable inventory
to be of type Inventory{}
. Then two IceCream structs are created and appended to the inventory.
Finally, we print our inventory and get the following:
{[{Vanilla 5.99 10} {Chocolate 7.99 12}]}
If we sell 2 pints of Vanilla ice cream, we could use the following code to update our inventory.
inventory.Items[0].Quantity -= 2
fmt.Println(inventory)
Dot-notation is used to reference the first element in the Items slice in the inventory
struct, and 2 is subtracted.
The print statement outputs:
{[{Vanilla 5.99 8} {Chocolate 7.99 12}]}
Just like Classes in other languages, structs can have methods associated with them. However, in Go, they’re defined outside the struct, which seems a little weird if you’re familiar with other languages.
To create a struct method, a function is defined using a receiver value to the struct.
In the example below, we create a value()
method with an (i IceCream)
receiver value. We can call the method for any IceCream struct we have using icecream.value()
. The float64 portion is the return value.
Inside the method, the value i
is the equivalent of the this or self keywords used in other languages. It references the struct that the method was called from and lets us access fields within the struct with a .
and the field name.
package main
import "fmt"
type IceCream struct {
Flavor string
Price float64
Quantity int
}
func (i IceCream) value() float64 {
return i.Price * float64(i.Quantity)
}
func main() {
icecream := IceCream{
Flavor: "Vanilla",
Price: 5.99,
Quantity: 10,
}
fmt.Printf("We have $%.2f worth of Vanilla ice cream.", icecream.value())
}
When we call icecream.value()
we are taking the icecream
struct and passing it into the value()
method. The struct’s field values for Price (10) and Quantity (5.99) are multiplied together and returned, resulting in the output:
We have $59.90 worth of Vanilla ice cream.
In this post, we looked at what structs are in Go and how they’re used. We explored the syntax of creating a struct, how to change field values, and the different ways to print the struct. We also looked at how to define struct methods and how to call them.
In the next post, we learn more about functions Go functions. Check out the Go page to see other Go lessons too.