Guarded closures

IMHO here we are mixing two different proposals that solve different issues, and there is no need for opting for one or the other (I would love to have both):

a) Allowing shadowing and rebinding of self [recent discussion, old evolution proposal, recently merged implementation] (please note that this proposal is not being discussed here, I am just considering it in order to explain why I think it is different): it intents to modify the usage of the self keyword. This is an important change for the language, and not syntactic sugar, because (as I understood) it basically proposes to stop treating self as a reserved keyword and start treating it as any other identifier. This allows to unwrap self by reasigning it, instead of requiring a new identifier:

Before:
    let closure: () -> Void = { [weak self] in
        if let strongSelf = self {
            strongSelf.found = true
        } else {
            strongSelf.found = false
        }
    }

After:
    let closure: () -> Void = { [weak self] in
        if let self = self {
            self.found = true
        } else {
            self.found = false
        }
    }

b) Introducing a guard keyword in a capture list [recent discussion, Unofficial evolution proposal]: this introduces a guard keyword as part of the capture list of a closure, which is not only intended to be useful when capturing self, but can also be used to capture any other identifiers:

Before:
    let closure: () -> Void = { [weak self] in
        guard let strongSelf = self else {
          return
        }
        strongSelf.intProperty += 1
    }

After:
    let closure: () -> Void = { [guard self] in
        self.intProperty += 1
    }

This is syntactic sugar for a specific closure usage case, but it is worth considering because this usage case appears a lot. In order to be eligible to use guard in a capture list, the closure has to satisfy that:

  1. The closure intents to capture one or several properties weakly.
  2. The code inside the closure is only intended to be executed if all the weakly captured properties are not nil.
  3. The closure returns Void.

The rationale I have in mind for this proposal is extremely similar to the rationale given for the recently accepted toggle() proposal:

  • commonality: the need to unwrap a weakly captured identifier is a common one across multiple different problem domains

  • readability: the addition of this command can significantly improve readability for the cases in which it is possible to use it (which are many, despite the requirements that the closure has to comply with)

  • consistency: while capturing weakly and then unwrapping is fairly trivial, it is good to have a consistent way of doing this across all swift codebases, rather than having multiple variants (with multiple variants, I mean that the identifier given when unwrapping may be different in every case. For example, when unwrapping self, developers assign the new strong reference to indentifiers such as weakSelf, strongSelf, ss, sSelf, wSelf...)

  • correctness: being able to give a different identifier for the unwrapped and weakly (optional) captured object, can lead to this not-so-correct code (which eventually can lead to bugs):

      let closure: () -> Void = { [weak self] in
          guard let strongSelf = self else {
            return
          }
          strongSelf.intProperty += 1
          self?.anotherIntProperty += 1
      }