// MODULE 03

Structs Deep Dive

Go's alternative to classes. Composition over inheritance, methods, and struct tags.

Structs vs Classes

Go doesn't have classes. Instead, it has structs + methods. It's simpler and more flexible.

Python Class
class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def greet(self):
        return f"Hi, I'm {self.name}"

u = User("Alice", 30)
Go Struct
type User struct {
    Name string
    Age  int
}

func (u User) Greet() string {
    return fmt.Sprintf("Hi, I'm %s", u.Name)
}

u := User{Name: "Alice", Age: 30}

Creating Structs

Different ways to create structs
type User struct {
    Name  string
    Email string
    Age   int
}

// Named fields (preferred)
u1 := User{Name: "Alice", Email: "alice@example.com", Age: 30}

// Positional (fragile, avoid)
u2 := User{"Bob", "bob@example.com", 25</span|}

// Zero value struct
var u3 User  // All fields are zero values

// Pointer to struct
u4 := &User{Name: "Charlie"</span|}

// Access fields
fmt.Println(u1.Name)
u1.Age = 31

Methods

Methods are functions with a receiver — the struct they operate on.

Value vs Pointer Receivers
type Counter struct {
    count int
}

// Value receiver: gets a COPY
func (c Counter) Value() int {
    return c.count
}

// Pointer receiver: modifies original
func (c *Counter) Increment() {
    c.count++
}

c := Counter{}
c.Increment()
fmt.Println(c.Value())  // 1

Rule: If any method needs a pointer receiver, make ALL methods use pointer receivers for consistency.

Struct Embedding (Composition)

Go doesn't have inheritance. Instead, embed structs to compose behavior.

Embedding
type Person struct {
    Name string
    Age  int
}

func (p Person) Greet() string {
    return "Hi, I'm " + p.Name
}

// Employee embeds Person
type Employee struct {
    Person          // Embedded (no field name)
    Company string
    Salary  int
}

e := Employee{
    Person:  Person{Name: "Alice", Age: 30},
    Company: "Acme",
    Salary:  100000,
}

// Access embedded fields directly
fmt.Println(e.Name)     // "Alice" - promoted from Person
fmt.Println(e.Greet())  // "Hi, I'm Alice" - method promoted

Composition over Inheritance: This is more flexible than inheritance. You can embed multiple types and override methods.

Struct Tags

Tags add metadata to fields — used for JSON, database mapping, validation, etc.

Struct tags
type User struct {
    ID        int       `json:"id" db:"user_id"`
    Name      string    `json:"name"`
    Email     string    `json:"email" validate:"required,email"`
    Password  string    `json:"-"`              // Excluded from JSON
    CreatedAt time.Time `json:"created_at,omitempty"`
}

// JSON encoding respects tags
user := User{ID: 1, Name: "Alice", Password: "secret"}
data, _ := json.Marshal(user)
// {"id":1,"name":"Alice","email":""}
// Note: Password excluded, CreatedAt omitted (zero value)

Constructor Functions

Go doesn't have constructors. Use factory functions instead.

Constructor pattern
type Server struct {
    host    string
    port    int
    timeout time.Duration
}

// Constructor function (convention: New + TypeName)
func NewServer(host string, port int) *Server {
    return &Server{
        host:    host,
        port:    port,
        timeout: 30 * time.Second, // Default value
    }
}

// With validation
func NewServer(host string, port int) (*Server, error) {
    if port < 1 || port > 65535 {
        return nil, errors.New("invalid port")
    }
    return &Server{host: host, port: port}, nil
}

Exercises

Progress through each section in order, or jump to where you need practice.

Practice individual concepts you just learned.

💪 Challenges

Combine concepts and learn patterns. Each challenge has multiple variants at different difficulties.

Module 3 Summary