# Defining Structs
Structs group related data together. They're Go's primary way to create custom types — there are no classes.
type User struct {
Name string
Email string
Age int
}
func main() {
// Named fields
u1 := User{Name: "Alice", Email: "alice@go.dev", Age: 30}
// Pointer to struct
u2 := &User{Name: "Bob", Age: 25}
fmt.Println(u1.Name) // Alice
fmt.Println(u2.Name) // Bob (auto-dereferenced)
}# Methods
Methods are functions with a receiver. Use a pointer receiver *T to modify the struct:
type Rect struct {
Width, Height float64
}
// Value receiver — doesn't modify
func (r Rect) Area() float64 {
return r.Width * r.Height
}
// Pointer receiver — can modify
func (r *Rect) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
r := Rect{Width: 3, Height: 4}
fmt.Println(r.Area()) // 12
r.Scale(2)
fmt.Println(r.Area()) // 48
}# Interfaces — Implicit Contracts
Interfaces define behavior, not data. A type satisfies an interface simply by implementing its methods — noimplements keyword needed.
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// Circle implicitly satisfies Shape!
func printInfo(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n",
s.Area(), s.Perimeter())
}# Struct Embedding (Composition)
Go prefers composition over inheritance. Embed one struct inside another to reuse its fields and methods:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return a.Name + " makes a sound"
}
type Dog struct {
Animal // embedded (no field name)
Breed string
}
func main() {
d := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Labrador",
}
fmt.Println(d.Name) // promoted: Rex
fmt.Println(d.Speak()) // promoted: Rex makes a sound
}⚡ Key Takeaways
- Go has structs, not classes — behavior is added via methods with receivers
- Use pointer receivers
*Twhen mutating or for large structs - Interfaces are satisfied implicitly — no
implementskeyword - Embedding promotes fields & methods — composition over inheritance
- Keep interfaces small (1-3 methods) — Go's stdlib follows this religiously