Go #006 – Conditionals and Loops: Control Flow Without the Clutter
Go’s conditionals and loops are minimalist by design, but their simplicity hides precise memory decisions. Let’s explore how if, switch, and for interact with the stack, heap, and data segments—and where hidden allocations creep in. The Code package main func main() { // Conditional with stack-allocated variable if score := 42; score > 50 { // ... } // Loop with slice iteration nums := []int{10, 20, 30} // Slice header (stack), backing array (heap) for i, n := range nums { // ... } // Loop closure capturing variable for _, n := range nums { func() { println(n) // n escapes to heap }() } } Memory Segmentation Visualized +-------------------+ Lowest Address | Code | |-------------------| | main() | // Compiled logic for conditionals/loops | range handling | // Machine code for slice iteration +-------------------+ | Data | |-------------------| | (No constants) | +-------------------+ | Stack | |-------------------| | score (int = 42) | // if-scoped variable | nums (slice) | // Pointer to heap array | i, n (loop vars) | // Per-iteration copies +-------------------+ | Heap | |-------------------| | []int backing | // nums' array [10, 20, 30] | n (int copies) | // Captured by closure (one per iteration) +-------------------+ Highest Address Breakdown of Memory Behavior 1. Conditionals (if, switch) Variables declared in if/switch: score in if score := 42; ... is stack-allocated. Scoped to the block, cleaned up immediately. No implicit heap allocations unless variables escape (e.g., passed to a closure). 2. Loops (for, range) Loop variables: i and n in for i, n := range nums are reused per iteration (stack-allocated). Values are copied, so modifying n doesn’t affect the original slice. Slice backing array: nums := []int{10, 20, 30} stores the slice header (pointer, len, cap) on the stack. The actual array [10, 20, 30] lives on the heap. 3. Closures in Loops Captured variables: n in the closure func() { println(n) }() escapes to the heap. Each iteration’s n is copied to the heap to outlive the loop iteration. Verify with escape analysis: go build -gcflags="-m" main.go # ./main.go:12:3: ... n escapes to heap Why This Matters Loop variable reuse: Go reuses the same memory address for i and n in each iteration. Gotcha: Goroutines in loops capturing loop variables (e.g., go func() { ... }()) will capture the latest value of i/n. Fix: for _, n := range nums { n := n // Create a stack copy per iteration go func() { println(n) }() } Slice efficiency: Large slices force heap allocations for backing arrays. Pre-size with make to avoid reallocations. Optimization Tips Avoid closures in hot loops: Use explicit parameters to keep variables on the stack. Pre-allocate slices: data := make([]int, 0, 1000) // Heap-allocated but avoids reallocations Beware of interface{} in conditions: var val interface{} = 42 // Heap escape if val == 42 { ... } // Type check overhead Key Insight: Go’s for loops reuse variables to minimize allocations—a design choice that’s fast but demands caution with closures and goroutines.

Go’s conditionals and loops are minimalist by design, but their simplicity hides precise memory decisions. Let’s explore how if
, switch
, and for
interact with the stack, heap, and data segments—and where hidden allocations creep in.
The Code
package main
func main() {
// Conditional with stack-allocated variable
if score := 42; score > 50 {
// ...
}
// Loop with slice iteration
nums := []int{10, 20, 30} // Slice header (stack), backing array (heap)
for i, n := range nums {
// ...
}
// Loop closure capturing variable
for _, n := range nums {
func() {
println(n) // n escapes to heap
}()
}
}
Memory Segmentation Visualized
+-------------------+ Lowest Address
| Code |
|-------------------|
| main() | // Compiled logic for conditionals/loops
| range handling | // Machine code for slice iteration
+-------------------+
| Data |
|-------------------|
| (No constants) |
+-------------------+
| Stack |
|-------------------|
| score (int = 42) | // if-scoped variable
| nums (slice) | // Pointer to heap array
| i, n (loop vars) | // Per-iteration copies
+-------------------+
| Heap |
|-------------------|
| []int backing | // nums' array [10, 20, 30]
| n (int copies) | // Captured by closure (one per iteration)
+-------------------+ Highest Address
Breakdown of Memory Behavior
1. Conditionals (if
, switch
)
-
Variables declared in
if
/switch
:-
score
inif score := 42; ...
is stack-allocated. - Scoped to the block, cleaned up immediately.
-
- No implicit heap allocations unless variables escape (e.g., passed to a closure).
2. Loops (for
, range
)
-
Loop variables:
-
i
andn
infor i, n := range nums
are reused per iteration (stack-allocated). - Values are copied, so modifying
n
doesn’t affect the original slice.
-
-
Slice backing array:
-
nums := []int{10, 20, 30}
stores the slice header (pointer, len, cap) on the stack. - The actual array
[10, 20, 30]
lives on the heap.
-
3. Closures in Loops
-
Captured variables:
-
n
in the closurefunc() { println(n) }()
escapes to the heap. - Each iteration’s
n
is copied to the heap to outlive the loop iteration.
-
- Verify with escape analysis:
go build -gcflags="-m" main.go
# ./main.go:12:3: ... n escapes to heap
Why This Matters
-
Loop variable reuse: Go reuses the same memory address for
i
andn
in each iteration.-
Gotcha: Goroutines in loops capturing loop variables (e.g.,
go func() { ... }()
) will capture the latest value ofi
/n
. Fix:
for _, n := range nums { n := n // Create a stack copy per iteration go func() { println(n) }() }
-
Gotcha: Goroutines in loops capturing loop variables (e.g.,
Slice efficiency: Large slices force heap allocations for backing arrays. Pre-size with
make
to avoid reallocations.
Optimization Tips
- Avoid closures in hot loops: Use explicit parameters to keep variables on the stack.
- Pre-allocate slices:
data := make([]int, 0, 1000) // Heap-allocated but avoids reallocations
- Beware of interface{} in conditions:
var val interface{} = 42 // Heap escape
if val == 42 { ... } // Type check overhead
Key Insight: Go’s for
loops reuse variables to minimize allocations—a design choice that’s fast but demands caution with closures and goroutines.