Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Part 3: Goroutines, Channels & Control Flow
GO

Part 3: Goroutines, Channels & Control Flow

Review goroutines, channels, select, defer, panic, recover, and important Go control-flow patterns.

1. Goroutines

  • A goroutine is a lightweight thread managed by the Go runtime — cheap (KB-stack, scales to millions).
  • go f() launches one; the current function continues; the program doesn’t wait.
  • Race risk: two goroutines touching shared state without synchronization is a data race — fix with channels or sync primitives.
  • No built-in way to wait for a goroutine to finish without a channel/WaitGroup.

2. Channels

  • A channel is a typed conduit for communication + synchronization.
  • ch := make(chan T) — unbuffered (synchronous: send blocks until receive).
  • ch := make(chan T, n) — buffered: send blocks only when full.
  • ch <- v send; v := <-ch receive; close(ch) stops further sends.
  • Unbuffered send/receive pair syncs — the sender blocks until a receiver is ready.
ch := make(chan int)
go func() { ch <- 42 }()
v := <-ch        // blocks until value arrives

Channel directions

  • chan T — bidirectional; chan<- T write-only (sender); <-chan T read-only (receiver).
  • Directional types are useful for APIs.

Closing & ranging

  • close only on sender side; receiving from a closed channel returns the zero value immediately.
  • for v := range ch loops until the channel is closed.
  • Check: v, ok := <-chok false once closed and drained.

3. select

  • select waits on multiple channel operations — picks one ready case (random if several ready); default runs when none ready.
select {
case v := <-ch1:
    fmt.Println("got", v)
case ch2 <- 5:
    fmt.Println("sent")
case <-time.After(2 * time.Second):
    fmt.Println("timeout")
}
  • Used for timeouts, graceful shutdown, priority interpolation, etc.
  • Non-blocking check with an else/default.

4. defer / panic / recover

  • defer — executes on function exit, LIFO order (last deferred runs first).
  • Args evaluated at defer time; the function body args are evaluated when deferred (not at return).
  • panic — unwinds stack, running defers; if unhandled, program exits.
  • recover catches a panic — only useful inside a deferred function.
defer func() {
    if r := recover(); r != nil { fmt.Println("recovered", r) }
}()
somePanickingCall()

Golden rule: don’t use panic for routine errors — return errors instead.

5. Control flow — the differences

  • Go has no whilefor with condition covers it.
  • for {} — infinite; for range over slices/strings/maps/channels/ints.
  • defer is Go’s finally; it runs on normal and panic returns.
  • No ternary operator — write an if/else.
  • switch without an expression is a chainable if/else; switch x := ...; x supports init statement.
  • goto exists but discouraged.

Interview checkpoint:

  • Unbuffered vs buffered channel semantics (send/receive block rules).
  • Data races and synchronization via channels / WaitGroup / mutex.
  • select + timeout patterns.
  • defer LIFO + arg evaluation timing.
  • Panic/recover placement (deferred) and the “don’t use it for normal errors” principle.

My Private Notes

Notes are auto-saved locally to this device.