Go (Golang) Tutorial 0/45 lessons ~6 min read Lesson 15

    Structs

    Structs group named fields into a single type — Go's primary data composition mechanism.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Structs group named fields into a single type — Go's primary data composition mechanism. Structs are value types: assignment copies all fields. Embedded structs (anonymous fields) enable composition over inheritance, a core Go design philosophy.

    Struct tags on fields drive JSON encoding, database column mapping, and validation. Understanding field visibility (exported vs unexported) and zero-value initialization builds the foundation for domain models in microservices.

    Interviewers ask struct vs class, embedding vs inheritance, and memory layout — this lesson covers all three with production examples.

    The story

    A Docker Compose metadata service models each container as a struct with image name, port mappings, and health-check interval. Structs group related fields with named types — when marshaled to JSON for a REST API, field tags control serialization without hand-writing parsers.

    Embedded structs compose configuration: a BaseService with logging fields gets promoted into PaymentService, mirroring how Kubernetes CRD specs embed standard metadata across hundreds of custom resources.

    Understanding the topic

    Key concepts

    • type Person struct { Name string; Age int } — value type.
    • Field visibility: uppercase exported, lowercase package-private.
    • Struct literals: Person{Name: "Alice", Age: 30} or positional Person{"Alice", 30}.
    • Embedding: type Admin struct { User; Level int } promotes User fields.
    • Struct tags: `json:"name" db:"user_name"` for marshaling/ORM.
    • Comparable structs if all fields comparable — usable as map keys.

    Step-by-step explanation

    1. Define type with struct keyword and field list.
    2. Create with literal or var zero initialization.
    3. Access fields with dot notation; pointer fields use automatic dereference.
    4. Embedded fields promote methods and fields to outer type.
    5. Pass struct by value (copy) or pointer (*Struct) for mutation.
    6. reflect or encoding/json reads tags at runtime.

    Practical code example

    Domain struct with embedding, tags, and constructor pattern:

    go
    package main
    import (
    "encoding/json"
    "fmt"
    "time"
    )
    type User struct {
    ID string `json:"id"`
    Email string `json:"email"`
    CreatedAt time.Time `json:"created_at"`
    }
    type Admin struct {
    User
    Role string `json:"role"`
    }
    func NewUser(id, email string) User {
    return User{ID: id, Email: email, CreatedAt: time.Now()}
    }
    func main() {
    admin := Admin{
    User: NewUser("u1", "admin@techlearningpro.com"),
    Role: "superadmin",
    }
    data, _ := json.Marshal(admin)
    fmt.Println(string(data))
    fmt.Println("email via promotion:", admin.Email)
    }

    Output

    {"id":"u1","email":"admin@techlearningpro.com","created_at":"...","role":"superadmin"}

    Line-by-line code explanation

    • type Container struct { Image string; Ports []int } defines a new structured type.
    • field tags `json:"image"` control JSON encoding names and omitempty behavior.
    • c := Container{Image: "nginx:1.25"} uses a struct literal with named fields.
    • c.Image accesses fields with dot notation — exported fields start with uppercase.
    • type PaymentService struct { BaseService; DBURL string } embeds another struct anonymously.
    • ps.LogLevel promotes embedded fields to the outer type's namespace.
    • cmp.Equal(a, b) (Go 1.21+) compares structs field-by-field in tests.
    • pointer to struct (*Container) avoids copying large structs when passing to functions.

    Key takeaway: Prefer constructor functions (NewUser) for invariants. Embedding promotes fields — not inheritance. Tags must match serializer conventions.

    Real-world use

    Where you'll use this in production

    • Domain entities: User, Order, Product in microservices.
    • Configuration structs loaded from env or YAML.
    • API request/response DTOs with json tags.
    • Database models with db or gorm tags.

    Best practices

    • Use constructor functions to enforce valid initial state.
    • Prefer composition (embedding) over copying fields.
    • Keep structs focused — split large structs by responsibility.
    • Use pointer receivers when methods mutate state.
    • Align struct tags with API contract (snake_case JSON common).
    • Document exported struct fields in godoc.

    Common mistakes

    • Huge god structs with 20+ fields — split by domain.
    • Exporting all fields when validation needed — use constructor.
    • Copying large structs in hot paths — use pointers.
    • Wrong json tag names breaking API clients.
    • Embedding conflicting field names — inner wins, outer shadowed.

    Advanced interview questions

    Q1BeginnerStruct vs class?
    Go has structs only — no classes; methods attached via receivers; composition over inheritance.
    Q2BeginnerValue vs pointer struct?
    Value copies on assign/pass; pointer shares single instance, enables mutation.
    Q3IntermediateEmbedding purpose?
    Composition — promotes embedded type's fields/methods to outer type without inheritance.
    Q4IntermediateStruct tags usage?
    Metadata for json, xml, db, validate — read by encoding/json and ORMs via reflection.
    Q5AdvancedDesign immutable Order with line items in Go.
    struct with unexported fields; constructor; methods return new Order; slices.Clone on items.

    Summary

    Structs compose data; embedding enables composition over inheritance. Exported fields (uppercase) are public API of the package. Struct tags drive JSON, DB, and validation serialization. Use constructors to enforce invariants on creation. Next lesson: methods — attaching behavior to types.

    Ready to mark this lesson complete?Track your journey across the entire course.