Why does this print the new city without a capture list, and the old one with [city]?

1.5.5 · L2 · Interview

Predict first

What do the two prints show?

struct City { var name: String }

var city = City(name: "Pune")

let byReference = { city.name }
let byValue = { [city] in city.name }

city.name = "Goa"

print(byReference(), byValue())
  • Goa Goa
  • Goa Pune
  • Pune Pune
  • Pune Goa

Mental model. Without the list you left a note saying “look at the whiteboard”. With it you photographed the whiteboard.

city is a struct stored in a variable. A closure that mentions city without a capture list captures the variable slot — so it reads whatever value is there when the closure runs. Mutating city afterwards changes what the closure sees.

[city] evaluates city immediately and copies the value into the closure's context. Later mutation of the variable cannot reach that copy.

Without a capture list the closure captures the variable, so it sees the change. [city] copied the value when the closure was created.

Code

struct City { var name: String }

var city = City(name: "Pune")

let byReference = { city.name }      // captures the variable slot
let byValue     = { [city] in city.name }  // captures a copy

city.name = "Goa"

print(byReference())  // ?
print(byValue())      // ?
Goa  /  Pune

Common traps

  • “Structs are always copied into closures.” Only with a capture list. Without one, the closure captures the variable, not the value.
  • Assuming this only matters for structs. For a class, the “variable” and the object are different things — capture lists change ownership, not the object identity.

They might follow up with

  • What if city were a class? Both closures would see the same object and print the new name — unless the variable was reassigned to a different instance.
  • What if the closure is @escaping? Same rule; the capture happens at creation either way. Escaping only affects where the context lives.

Related questions

  • 1.5.4 What does a capture list like [weak self], [unowned self], [self], or [foo] actually do?
  • 1.5.3 Why are closures called reference types? What is a closure context?
  • 1.5.6 unowned var view = UIView() then print(view) — what happens?

← What does a capture list like [weak self], [unowned self], [self], or…   unowned var view = UIView() then print(view) — what happens? →