Copying a closure in Swift

My understanding is that closures are reference types, so doing something like the following will result in both constants referring to the same instance of the closure:

let closureA = { print("I'm in a closure") }
let closureB = closureA

In Obj-C I believe there was a way to make a copy of a block.

Is there a way to make a copy of a closure in Swift?

The copying mechanism in ObjC is just for getting the block off the stack; once it’s off the stack, β€œcopying” just retains the existing value. That detail is something that Swift hides from you.

3 Likes

Closure variables have value semantics. (But are presumably only copy‐on‐write like structures.)

var closure: (Int) -> Void = { print("\($0): I’m still the first closure.") }
closure(1) // 1: I’m still the first closure.

var newVariable = closure
newVariable(2) // 2: I’m still the first closure.

closure = { print("\($0): I’ve been mutated.") }
closure(3) // 3: I’ve been mutated.
newVariable(4) // 4: I’m still the first closure.

On second thought, that’s not really mutation but reassignment.

Since closures aren’t mutable in the first place, I’m not sure what difference it makes, or why you would need a copy instead of a reference. What is it you are trying to do?

1 Like

Closures are meant to be blocks of functionality, they don't hold stored state or properties. So I'm also curious what the intention of OP is.

That's not true, they do hold state and that state isn't copied when you copy the closure itself. Check out this snippet:

func giveMeAClosureThatHoldsState() -> ( (Int) -> Int ) {
    var state = 0
    return { difference in
        state += difference
        return state
    }
}

let closureWithState = giveMeAClosureThatHoldsState()
let closureCopy = closureWithState
let anotherClosureWithState = giveMeAClosureThatHoldsState()
print(closureWithState(1)) // prints 1
print(closureWithState(1)) // prints 2
print(closureCopy(1)) // prints 3
print(anotherClosureWithState(1)) // prints 1
3 Likes

As others have explained above, closures don't really copy.

Yet there is a closure feature which is somewhat related: the capture list.

The capture list allows a closure to copy the values of captured variables, and this makes a big difference when captured variables are mutable. Compare:

var value = 0
let closure1 = { return value }
let closure2 = { [value] in return value }
print("closure1: \(closure1())") // prints "closure1: 0"
print("closure2: \(closure2())") // prints "closure2: 0"

value = 42
print("closure1: \(closure1())") // prints "closure1: 42"
print("closure2: \(closure2())") // prints "closure2: 0"

See Documentation for more information.

4 Likes

I guess you meant to say "they do hold state and state is copied when you copy the closure itself" -> print(closureWithCopy(1)) results in "3" as opposed to "2" (which imo would mean the state isn't copied) ?

Here's what going on after each line. Notice how both closureWithState and closureCopy point to the same state, so calling closureWithState changes what closureCopy returns

let closureWithState = giveMeAClosureThatHoldsState()
//   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   
//   β”‚state: 0 β”‚   
//   β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜   
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
// closureWithState

let closureCopy = closureWithState

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 
//    β”‚state: 0 β”‚                 
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                 
//        β”‚  β”‚                    
//        β”‚  β”‚                    
//        β”‚  └────────────┐       
//        β”‚               β”‚       
//        β”‚               β”‚       
//        β”‚               β”‚       
// closureWithState  closureCopy  

let anotherClosureWithState = giveMeAClosureThatHoldsState()

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 0 β”‚                        β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚  β”‚                                  β”‚            
//        β”‚  β”‚                                  β”‚            
//        β”‚  └────────────┐                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureWithState(1)) // prints 1

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 1 β”‚                        β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚  β”‚                                  β”‚            
//        β”‚  β”‚                                  β”‚            
//        β”‚  └────────────┐                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureWithState(1)) // prints 2

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 2 β”‚                        β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚  β”‚                                  β”‚            
//        β”‚  β”‚                                  β”‚            
//        β”‚  └────────────┐                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureCopy(1)) // prints 3

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 3 β”‚                        β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚  β”‚                                  β”‚            
//        β”‚  β”‚                                  β”‚            
//        β”‚  └────────────┐                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(anotherClosureWithState(1)) // prints 1

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 4 β”‚                        β”‚state: 1 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β–²β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚  β”‚                                  β”‚            
//        β”‚  β”‚                                  β”‚            
//        β”‚  └────────────┐                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

Here's what it would look like if copying a closure copied the state too

let closureWithState = giveMeAClosureThatHoldsState()
//   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   
//   β”‚state: 0 β”‚   
//   β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜   
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
//       β”‚         
// closureWithState

let closureCopy = closureWithState

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  
//    β”‚state: 0 β”‚  β”‚state: 0 β”‚  
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜  
//        β”‚               β”‚     
//        β”‚               β”‚     
//        β”‚               β”‚     
//        β”‚               β”‚     
//        β”‚               β”‚     
//        β”‚               β”‚     
// closureWithState  closureCopy

let anotherClosureWithState = giveMeAClosureThatHoldsState()

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 0 β”‚  β”‚state: 0 β”‚           β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureWithState(1)) // prints 1

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 1 β”‚  β”‚state: 0 β”‚           β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureWithState(1)) // prints 2

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 2 β”‚  β”‚state: 0 β”‚           β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(closureCopy(1)) // ❗should print 1, because it copied state when it was still zero❗

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 2 β”‚  β”‚state: 1 β”‚           β”‚state: 0 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState

print(anotherClosureWithState(1)) // prints 1, because it was created with it's own state

//    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         
//    β”‚state: 2 β”‚  β”‚state: 1 β”‚           β”‚state: 1 β”‚         
//    β””β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β–²β”€β”€β”˜         
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
//        β”‚               β”‚                     β”‚            
// closureWithState  closureCopy      anotherClosureWithState