Go
02 / 04

Interfaces, Goroutines & Channels

Interfaces, Goroutines & Channels

Interfaces

// Interface — implicit implementation (no "implements")
type Animal interface {
    Sound() string
    Name() string
}

type Dog struct{ name string }
func (d Dog) Sound() string { return "Woof" }
func (d Dog) Name() string { return d.name }

type Cat struct{ name string }
func (c Cat) Sound() string { return "Meow" }
func (c Cat) Name() string { return c.name }

func describe(a Animal) {
    fmt.Printf("%s says %s
", a.Name(), a.Sound())
}

// Works for any type implementing Animal
describe(Dog{"Rex"})
describe(Cat{"Whiskers"})

// Common interfaces (stdlib)
// io.Reader — Read(p []byte) (n int, err error)
// io.Writer — Write(p []byte) (n int, err error)
// fmt.Stringer — String() string
// error — Error() string

// Empty interface — any type
var any interface{} = 42
any = "hello"
any = Dog{}

// Type assertion
s, ok := any.(string)
if !ok { ... }

// Type switch
switch v := any.(type) {
case int:
    fmt.Printf("int: %d
", v)
case string:
    fmt.Printf("string: %s
", v)
default:
    fmt.Printf("other: %T
", v)
}

Error Handling

// Idiomatic Go: explicit error returns
func fetchUser(id int) (*User, error) {
    if id <= 0 {
        return nil, fmt.Errorf("invalid id: %d", id)
    }
    // ...
    return &User{ID: id}, nil
}

// Check errors immediately
user, err := fetchUser(5)
if err != nil {
    return fmt.Errorf("fetchUser failed: %w", err)  // wrap with context
}

// Custom error type
type NotFoundError struct {
    Resource string
    ID       int
}
func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s with id %d not found", e.Resource, e.ID)
}

// Unwrap errors
var nfe *NotFoundError
if errors.As(err, &nfe) {
    fmt.Println("not found:", nfe.Resource)
}
if errors.Is(err, ErrTimeout) { ... }  // compare sentinel errors

// Panic/recover (only for truly unexpected failures)
defer func() {
    if r := recover(); r != nil {
        fmt.Println("recovered:", r)
    }
}()

Goroutines & Channels

// Goroutine — lightweight thread, launched with 'go'
go func() {
    fmt.Println("running concurrently")
}()

// Channel — typed pipe for communication between goroutines
ch := make(chan int)         // unbuffered — blocks until both sides ready
bch := make(chan int, 10)    // buffered — blocks when full

// Send / receive
go func() { ch <- 42 }()
val := <-ch                  // receive (blocks)

// Range over channel
go func() {
    for _, v := range []int{1, 2, 3} { ch <- v }
    close(ch)
}()
for v := range ch { fmt.Println(v) }   // until closed

// Select — wait on multiple channels
select {
case msg := <-ch1:
    fmt.Println("from ch1:", msg)
case msg := <-ch2:
    fmt.Println("from ch2:", msg)
case <-time.After(1 * time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("no message ready")
}

// WaitGroup — wait for goroutines to finish
var wg sync.WaitGroup
for _, url := range urls {
    wg.Add(1)
    go func(u string) {
        defer wg.Done()
        fetch(u)
    }(url)
}
wg.Wait()

// Mutex — protect shared state
var mu sync.Mutex
mu.Lock()
sharedMap[key] = value
mu.Unlock()

// sync.Once — run exactly once
var once sync.Once
once.Do(func() { initExpensiveResource() })

Context

import "context"

// Pass context for cancellation/timeout
func fetchUser(ctx context.Context, id int) (*User, error) {
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    // ...
}

// Timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
user, err := fetchUser(ctx, 1)

// Cancellation
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(2 * time.Second)
cancel()   // signal worker to stop

// Check cancellation in worker
func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("cancelled:", ctx.Err())
            return
        default:
            doWork()
        }
    }
}

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free