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 <- vsend;v := <-chreceive;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<- Twrite-only (sender);<-chan Tread-only (receiver).- Directional types are useful for APIs.
Closing & ranging
closeonly on sender side; receiving from a closed channel returns the zero value immediately.for v := range chloops until the channel is closed.- Check:
v, ok := <-ch—okfalse once closed and drained.
3. select
selectwaits on multiple channel operations — picks one ready case (random if several ready);defaultruns 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.recovercatches 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
while—forwith condition covers it. for {}— infinite;for rangeover slices/strings/maps/channels/ints.deferis Go’sfinally; it runs on normal and panic returns.- No ternary operator — write an
if/else. switchwithout an expression is a chainable if/else;switch x := ...; xsupports init statement.gotoexists but discouraged.
Interview checkpoint:
- Unbuffered vs buffered channel semantics (send/receive block rules).
- Data races and synchronization via channels / WaitGroup / mutex.
select+ timeout patterns.deferLIFO + arg evaluation timing.- Panic/recover placement (deferred) and the “don’t use it for normal errors” principle.
Premium Content
Unlock Part 3: Goroutines, Channels & Control Flow and all premium lessons with a subscription.
All premium lessons
Ad-free experience
Priority support
From ₹199.99/year — See plans