# Why Structs Matter
Structs are Go's primary way to model domain data. They keep related fields together, improve type safety, and make APIs self-documenting.
In production systems, clean struct design directly improves validation, serialization, and long-term maintainability.
Real-World Analogyclick to expand
A struct is like a well-defined form. Every field has a name and expected type, so everyone reading or writing the form follows the same contract.
# Defining and Initializing Structs
Struct zero values are useful by default: each field gets its own zero value. Named-field literals are preferred for readability and safer refactors.
type Person struct {
Name string
Age int
}
p1 := Person{Name: "Alice", Age: 30}
p2 := &Person{Name: "Bob", Age: 25}
var p3 Person# Embedding for Composition
Embedding promotes fields and methods from the embedded type. It is composition, not inheritance.
type Animal struct{ Name string }
func (a Animal) Speak() string { return a.Name + " speaks" }
type Dog struct {
Animal
Breed string
}
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Lab"}
fmt.Println(d.Name)
fmt.Println(d.Speak())# Struct Tags and Serialization
Tags add metadata consumed by reflection-based packages like JSON encoders and validators.
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"-"`
}# Practice Challenge
Define a Rectangle struct and implementArea and Perimeter. Bonus: add methods with pointer/value receiver rationale.
package main
import "fmt"
type Rectangle struct {
Width float64
Height float64
}
func Area(r Rectangle) float64 {
// TODO
}
func Perimeter(r Rectangle) float64 {
// TODO
}
func main() {
r := Rectangle{Width: 4, Height: 3}
fmt.Println("Area:", Area(r))
fmt.Println("Perimeter:", Perimeter(r))
}⚡ Key Takeaways
- Struct zero value is immediately usable
- Use named field literals for clarity and future-safe code
- Embedding enables composition and promoted methods
- Struct tags control JSON/DB/validation mapping behavior