Where do value types live — stack or heap?

1.1.3 · L3 · Learning

Predict first

What does this print?

struct Point { var x = 0, y = 0 }

func makeCounter() -> () -> Int {
    var p = Point()
    return { p.x += 1; return p.x }
}

let next = makeCounter()
print(next(), next())
  • 1, 1
  • 1, 2
  • Crash: p is gone

Mental model. “Structs are on the stack” is a useful lie, like “the sun rises”. It is directionally helpful and technically wrong.

It depends on escape analysis. A small struct that does not escape is usually stored inline in its stack frame or in registers. But a struct is moved to the heap when it is captured by an escaping closure, stored in an existential, or otherwise escapes the compiler's view of its lifetime.

It also depends on contents: Array is a struct, but its elements live in a heap buffer. So a “stack value” can absolutely own heap memory.

The escaping closure captured p, so it was boxed on the heap and outlived the function. Both calls mutate that same box. Step through the diagram below to see it move.

Common traps

  • “Structs are stack, classes are heap.” Both statements have exceptions. The real rule is about storage duration and escape analysis, not the keyword.
  • “Value types cannot be shared.” A struct can hold a class reference, a closure, or an unsafe pointer — all shared.

They might follow up with

  • How can you check? Use MemoryLayout for size/stride, and Instruments or a heap snapshot to see where an instance actually lives.
  • Why does it matter? Stack allocation is cheap and cache-friendly; heap allocation costs a malloc, a retain/release, and potential fragmentation.

Related questions

  • 1.1.1 What is the difference between a value type and a reference type in Swift? When do you choose each?
  • 1.1.7 What is copy-on-write? Which types use it?
  • 1.4.6 What is an existential container? What is a witness table?

← What is the difference between struct, class, and actor?   When would you still use a class in a Swift-first codebase? →