In this tutorial, we’ll learn about structs in Go. Structs let you group related data together and create custom types, similar to classes in other languages.
Understanding Structs in Go
Basic Struct Syntax
To declare and initialize a struct in Go, use the type
keyword to define a new type, followed by the type name (e.g., Book
), and the struct
keyword to specify its fields.
type Book struct {
Title string
Author string
Pages int
}
myBook := Book{
Title: "The Go Programming Language",
Author: "Alan A. A. Donovan",
Pages: 380,
}
This example defines a Book
struct with fields for title, author, and pages, and initializes an instance of it.
Alternatively, you can create an empty struct and assign values to its fields later.
var myBook Book
This will create an empty Book
struct.
To add values to the struct fields, you can use the following syntax:
myBook.Title = "The Go Programming Language"
myBook.Author = "Alan A. A. Donovan"
myBook.Pages = 380
fmt.Println(myBook) // Output will be: {The Go Programming Language Alan A. A. Donovan 380}
Accessing and Modifying Struct Fields
You can read values from a struct using the dot notation.
title := myBook.Title
fmt.Println(title) // Output will be "The Go Programming Language"
You can also directly mutate the fields of a struct using the dot notation. For example:
myBook.Title = "New Title"
fmt.Println(myBook.Title) // Output will be "New Title"
Nested Structs
Structs can be nested within other structs to create more complex data structures.
type Library struct {
Name string
Books []Book
}
myLibrary := Library{
Name: "City Library",
Books: []Book{
{Title: "The Go Programming Language", Author: "Alan A. A. Donovan", Pages: 380},
{Title: "Go in Action", Author: "William Kennedy", Pages: 300},
},
}
This example shows a Library
struct containing a slice of Book
structs.
To print a struct with all its fields, you can use the %+v
format specifier.
fmt.Printf("%+v", myLibrary)
// Output will be: {Name:City Library Books:[{Title:The Go Programming Language Author:Alan A. A. Donovan Pages:380} {Title:Go in Action Author:William Kennedy Pages:300}]}
Struct Methods
You can define methods on structs to add functionality. You write methods as functions with a special receiver argument, which is the struct type it belongs to.
func (b Book) Summary() string {
return fmt.Sprintf("%s by %s, %d pages", b.Title, b.Author, b.Pages)
}
fmt.Println(myBook.Summary())
This method returns a formatted string summarizing the book’s details.
Modifying Struct Fields with Methods
To update struct fields in a method, use a pointer receiver. A pointer is a reference to the memory address of the value it points to, so if you don’t use a pointer receiver, the method will only modify a copy of the struct, not the original.
func (b *Book) UpdatePages(newPages int) {
if newPages > 0 {
b.Pages = newPages
} else {
fmt.Println("Invalid page count. Pages must be greater than zero.")
}
}
myBook.UpdatePages(400)
fmt.Println(myBook.Pages) // Output will be 400
If we would not have used a pointer receiver, the changes would not have been reflected in the original struct.
func (b Book) UpdatePages(newPages int) {
if newPages > 0 {
b.Pages = newPages
} else {
fmt.Println("Invalid page count. Pages must be greater than zero.")
}
}
myBook.UpdatePages(400)
fmt.Println(myBook.Pages) // Output will be 380
Comparing Structs
In Go, you can compare structs using the ==
operator. For structs to be equal, all their fields must match exactly.
book1 := Book{Title: "Go in Action", Author: "William Kennedy", Pages: 300}
book2 := Book{Title: "Go in Action", Author: "William Kennedy", Pages: 300}
if book1 == book2 {
fmt.Println("The books are identical.")
} else {
fmt.Println("The books are different.")
}
The output will be “The books are identical.” because the fields match exactly.
Composition and Interfaces
Go doesn’t support inheritance. Instead, it uses composition to build complex types from simpler ones and interfaces for flexible behavior.
Example of Composition
package main
import "fmt"
type Printer struct{}
func (p Printer) Print() {
fmt.Println("Printing document...")
}
type Scanner struct{}
func (s Scanner) Scan() {
fmt.Println("Scanning document...")
}
// A struct that combines both Printer and Scanner
type MultiFunctionDevice struct {
Printer
Scanner
}
func main() {
// Create a new MultiFunctionDevice
mfd := MultiFunctionDevice{}
mfd.Scan()
mfd.Print()
}
Anonymous Structs
Anonymous structs are useful for temporary data structures, such as when parsing JSON. They are ideal for one-time use.
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonResponse := `{"Name": "Alice", "Age": 30}`
// Use an anonymous struct directly
person := struct {
Name string
Age int
}{}
err := json.Unmarshal([]byte(jsonResponse), &person)
if err != nil {
fmt.Println("Error unmarshalling JSON:", err)
return
}
fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
}
This example shows how to parse JSON data into an anonymous struct.
Conclusion
In this tutorial, we explored how to use structs in Go. We covered how to declare and initialize them, access and modify fields, nest structs, define methods, and compare structs. We also looked at using composition to build complex types. Finally, we briefly touched on anonymous structs for temporary data structures.
Full source code can be found here Github