iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Exercises

Six Go drills that exercise the standard library idioms.

Six Go exercises

EXAMPLE
// ============================================================
// Drill 1 — Read a file safely
// ============================================================
// TASK: read /etc/hostname, return the hostname (trimmed).
//
// ANSWER:
func hostname() (string, error) {
    b, err := os.ReadFile("/etc/hostname")
    if err != nil { return "", fmt.Errorf("read hostname: %w", err) }
    return strings.TrimSpace(string(b)), nil
}

// ============================================================
// Drill 2 — Walk a directory tree
// ============================================================
// TASK: find all .go files under ./src
//
// ANSWER:
func goFiles(root string) ([]string, error) {
    var out []string
    err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
        if err != nil { return err }
        if d.IsDir() || filepath.Ext(p) != ".go" { return nil }
        out = append(out, p)
        return nil
    })
    return out, err
}

// ============================================================
// Drill 3 — Concurrent HTTP fetches with a bounded pool
// ============================================================
// TASK: fetch N URLs with max 8 in flight.
//
// ANSWER:
func fetchAll(ctx context.Context, urls []string, concurrency int) []error {
    sem := make(chan struct{}, concurrency)
    errs := make([]error, len(urls))
    var wg sync.WaitGroup
    for i, u := range urls {
        wg.Add(1)
        sem <- struct{}{}
        go func(i int, u string) {
            defer wg.Done()
            defer func() { <-sem }()
            req, _ := http.NewRequestWithContext(ctx, "GET", u, nil)
            res, err := http.DefaultClient.Do(req)
            if err != nil { errs[i] = err; return }
            res.Body.Close()
        }(i, u)
    }
    wg.Wait()
    return errs
}

// ============================================================
// Drill 4 — JSON streaming
// ============================================================
// TASK: read a giant array of objects without loading it all.
//
// ANSWER:
func streamJSON(r io.Reader) error {
    dec := json.NewDecoder(r)
    t, err := dec.Token()      // expect [
    if err != nil || t != json.Delim("[") { return fmt.Errorf("expected array") }
    for dec.More() {
        var item map[string]any
        if err := dec.Decode(&item); err != nil { return err }
        // process item ...
    }
    dec.Token()                // closing ]
    return nil
}

// ============================================================
// Drill 5 — Time-bound a slow operation
// ============================================================
// TASK: call an HTTP endpoint with a 2-second timeout.
//
// ANSWER:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
res, err := http.DefaultClient.Do(req)
// err wraps context.DeadlineExceeded when the timeout fires

// ============================================================
// Drill 6 — Cancellable pipeline
// ============================================================
// TASK: a worker drains a channel of jobs; stops when ctx is cancelled.
//
// ANSWER:
func worker(ctx context.Context, jobs <-chan Job) {
    for {
        select {
        case <-ctx.Done():
            return
        case j, ok := <-jobs:
            if !ok { return }
            process(j)
        }
    }
}

// ============================================================
// Bonus — atomic vs mutex for a counter
// ============================================================
// ANSWER: for an int counter use sync/atomic (lockless, faster). For
// multi-field state, use sync.Mutex / RWMutex.
// var n atomic.Int64
// n.Add(1); n.Load()

// ============================================================
// Scoring
// 6 / 6 -> production-ready Go
// 4 / 6 -> bookmark go/cheatsheet
// < 4   -> read the Go std lib examples on pkg.go.dev

type Job struct{}
func process(Job) {}

Why it matters

Always thread a `context.Context` through I/O and worker code. Cancellation, deadlines, and clean shutdowns are the difference between "the service stops when SIGTERM arrives" and "kubectl rollout takes 5 minutes because nothing honours the deadline".

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Fill in: func add(a, b ____) int { return a + b }
Try it Yourself »

Discussion

Loading…