Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
14: Structs
I was working on a small project recently—a simple coffee shop ordering system—and I hit a wall that I think perfectly illustrates why we need structs. Initially, I thought I could just get by with basic variables. I mean, what's a coffee order? It's just a size, a roast, and maybe some milk.
The variable mess
I started off like this. I just declared a few variables to hold the details of a single order:
var orderSize = "Large"
var orderRoast = "Dark"
var orderMilk = "Oat"
var orderPrice = 4.50
That works fine for one coffee. But the moment I wanted to handle a second order, things got ugly. I found myself creating order2Size, order2Roast, and so on. I realized that if I wanted to pass this order to a function—say, a function that prints a receipt—I'd have to pass four different arguments every single time. It's tedious and, frankly, a great way to accidentally swap the roast and the milk in a function call.
Grouping the chaos
I need a way to tell Go, "These four pieces of data belong together as a single entity." I can't use an array because arrays require every element to be the same type, and I've got strings and floats mixed here. This is where I introduce a struct.
I'll define a custom type that acts as a blueprint for my order:
type CoffeeOrder struct {
Size string
Roast string
Milk string
Price float64
}
Now, instead of four loose variables, I have one CoffeeOrder. Let's see how I actually create one of these in the code:
func main() {
// I can initialize it by naming the fields
myOrder := CoffeeOrder{
Size: "Large",
Roast: "Dark",
Milk: "Oat",
Price: 4.50,
}
fmt.Println(myOrder)
}
When I run this, Go prints something like {Large Dark Oat 4.5}. It's not the prettiest output, but the data is bundled. I've essentially created my own complex data type.
Getting the data back out
Now that the data is bundled, I need to be able to use it. I can't just treat the myOrder variable as a string or a float; it's a CoffeeOrder. To get to the specific details, I use the dot operator. It's like saying, "Go inside this order and grab the Price."
fmt.Printf("Your %s %s roast is ready! That will be $%.2f",
myOrder.Size,
myOrder.Roast,
myOrder.Price)
This is much cleaner. If I decide later that I need to add a SugarLevel field to my order, I only have to update the struct definition and the places where I create the order. I don't have to hunt down every function signature in my app to add another string argument.
The "Zero Value" surprise
Here's something I noticed while playing around. What happens if I don't provide all the fields when I create the struct?
quickOrder := CoffeeOrder{Size: "Small"}
fmt.Println(quickOrder.Roast)
I expected maybe an error or a null, but Go just printed an empty string. This is because structs follow the same "zero value" rule as basic variables. If I don't specify a value for Roast, it defaults to the zero value for a string (""). For Price, it would be 0.0. It's handy, but it's something to watch out for—you don't want to accidentally charge a customer $0.00 just because you forgot to set the price field.
📋 Practical Task
Build a Server Health Monitor
You are building a tool to monitor the health of various servers in a data center. Instead of tracking server details in separate lists, you need to use a struct to keep the data organized.
Your Task:
- Define a struct named
ServerStatus. - Give it the following fields:
Hostname(string),IPAddress(string),CPUUsage(float64), andIsOnline(bool). - In your
mainfunction, create two different instances ofServerStatus(e.g., one for a "Web-Server" and one for a "DB-Server"). - Print a sentence for each server using its fields, for example:
"Server Web-Server (192.168.1.1) is Online with 12.5% CPU usage."
There are no comments for now.