[SOLVED] Why does this closure code throw an error when not using a capture list?

Issue

func biggerOne(_ a : Int, _ b : Int) -> Int? {
    if a == b {
        return nil
    } else if a > b {
        return a
    } else {
        return b
    }
}

var someClosure : (Int, Int) -> Int? = biggerOne(_:_:)

// Not working
someClosure = { (left : Int, right : Int) in
    someClosure(left , right)
}
print(someClosure(2,3)!)

// Working
someClosure = { [someClosure] (left : Int, right : Int) in
    someClosure(left , right)
}
print(someClosure(2,3)!)

I knew that closure uses capture list to store values ​​on creation so can solve the problem but why is the code above not working?

If you have any ideas, I would appreciate your help.

Solution

Without capturing, someClosure here is simply calling itself, and then causing a stack overflow:

someClosure = { (left : Int, right : Int) in
   someClosure(left , right) // this call refers to the new value of "someClosure"
}

(I’m not sure if the fact that this passes compilation is intentional.)

This is just like as if you have defined a named function like:

func someClosure(left: Int, right: Int): Int? {
    someClosure(left: left, right: right)
}

On the other hand, the capture list captures the old value of someClosure, so "someClosure" inside the closure body refers to the captured value (i.e. the old value), and there is no infinite recursion. You can think of this as:

let captured = someClosure
someClosure = { (left : Int, right : Int) in
    captured(left , right)
}

Using lldb yourExecutable and the thread backtrace command, you can see the many duplicated stack frames that are caused by the recursion.

(lldb) thread backtrace 
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ff7bf6ffff8)
  * frame #0: 0x00007ff822e4fa99 libswiftCore.dylib`swift_beginAccess + 41
    frame #1: 0x0000000100003d87 yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 71
    frame #2: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 124
    frame #3: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 124
    frame #4: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 124
    ...
    frame #65515: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 124
    frame #65516: 0x0000000100003dbc yourExecutable`$s14yourExecutableSiSgSi_SitcfU_ + 124
    frame #65517: 0x0000000100003baa yourExecutable`main + 218
    frame #65518: 0x00000001000154fe dyld`start + 462

Answered By – Sweeper

Answer Checked By – Robin (BugsFixing Admin)

Leave a Reply

Your email address will not be published.