Defer with local variable's state


(Sebastian Hagedorn) #1

We often write code that returns early, but has to make sure a bit of code (e.g., a completion handler) is always called whenever we return, which seems like a great use case for defer. I started to write this:

func execute(with completion: ((Bool) -> Void)?) {
  var success = false
  defer {
    // should always execute with the current state of success at that time
    completion?(success)
  }

  guard … else {
    // completion is expected to be executed with false
    return
  }

  success = true

  // completion is expected to be executed with true
}

However, it seems that defer copies the state of success, which means any update to the variable is not respected, and the completion block is always called with false.

Is there a way to make this work? I could image to call a function within the defer block that evaluates the success (e.g., depending on the state of an instance variable), but using a local variable seems to encapsulate this a lot better.

Thanks for reading and any advice in advance.
Sebastian


(Joe Groff) #2

This is a bug. Defer should track updates of the variable. Would you mind filing this at bugs.swift.org? Do you happen to know whether it reproduces only in debug or release builds, or both?

-Joe

···

On Aug 11, 2016, at 7:16 AM, Sebastian Hagedorn via swift-users <swift-users@swift.org> wrote:

We often write code that returns early, but has to make sure a bit of code (e.g., a completion handler) is always called whenever we return, which seems like a great use case for defer. I started to write this:

func execute(with completion: ((Bool) -> Void)?) {
  var success = false
  defer {
    // should always execute with the current state of success at that time
    completion?(success)
  }

  guard … else {
    // completion is expected to be executed with false
    return
  }

  success = true

  // completion is expected to be executed with true
}

However, it seems that defer copies the state of success, which means any update to the variable is not respected, and the completion block is always called with false.

Is there a way to make this work? I could image to call a function within the defer block that evaluates the success (e.g., depending on the state of an instance variable), but using a local variable seems to encapsulate this a lot better.


(Sebastian Hagedorn) #3

My bad. After double-checking my code, I realized that some branches were dispatching their work, which of course breaks that model. Sorry, and thanks for clarification that this is how it should works.

···

On 11 Aug 2016, at 17:39, Joe Groff <jgroff@apple.com> wrote:

On Aug 11, 2016, at 7:16 AM, Sebastian Hagedorn via swift-users <swift-users@swift.org> wrote:

We often write code that returns early, but has to make sure a bit of code (e.g., a completion handler) is always called whenever we return, which seems like a great use case for defer. I started to write this:

func execute(with completion: ((Bool) -> Void)?) {
  var success = false
  defer {
    // should always execute with the current state of success at that time
    completion?(success)
  }

  guard … else {
    // completion is expected to be executed with false
    return
  }

  success = true

  // completion is expected to be executed with true
}

However, it seems that defer copies the state of success, which means any update to the variable is not respected, and the completion block is always called with false.

Is there a way to make this work? I could image to call a function within the defer block that evaluates the success (e.g., depending on the state of an instance variable), but using a local variable seems to encapsulate this a lot better.

This is a bug. Defer should track updates of the variable. Would you mind filing this at bugs.swift.org? Do you happen to know whether it reproduces only in debug or release builds, or both?

-Joe