Revisiting requiring explicit `self.` when passing a method as an escaping closure

+1 for requiring self in this case to make capturing self obvious.


We may also support the weak case. Pseudocode:

// implicit or explicit self

webService.fetchKittenPhotos(completion: weak.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak.self.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak.foo.displayKittenPhotos)

// or this? which one would you prefer?

webService.fetchKittenPhotos(completion: weak displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak self.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak foo.displayKittenPhotos)

as an equivalent of:

webService.fetchKittenPhotos { [weak self] i in
    guard let self = self else { return /*see below*/ }
    return self.displayKittenPhotos(i)
}

This can work out of the box for closures returning Void / Optional, as otherwise it is not obvious what to return when self == nil. To have it work in all cases we can introduce a DefaultValue protocol:

protocol DefaultValue {
    static var `default`: Self { get }
}

struct Kitten { ... }
struct Puppy: DefaultValue { ... }
...
private func displayKittenPhoto(...) -> Kitten { ... }
private func displayPuppyPhoto(...) -> Puppy { ... }
...
webService.fetchKittenPhoto(completion: weak.displayKittenPhoto)
// Error: "self" can not be captured weakly here as "displayKittenPhoto"
// returns "Kitten" that has no default value. Fixit: Make "Kitten" implementing "DefaultValue" protocol.

webService.fetchPuppyPhoto(completion: weak.displayPuppyPhoto) // ok

.


Somewhat related: IIRC there was a topic recently about supporting "guard let self" syntax in closures and what to do in case of closures returning non void values. With the help of DefaultValue protocol we can make these two examples equivalent:

webService.fetchKittenPhotos { [weak self] i in
    guard let self
    displayKittenPhotos(i)
}

webService.fetchKittenPhotos { [weak self] i in
    guard let self = self else { return .default }
    displayKittenPhotos(i)
}

/* compiler magic
extension Void: DefaultValue {
    static var `default`: Self { .init() }
} 
*/

// in standard library:
extension Optional: DefaultValue {
    static var `default`: Self { nil }
}

I support this idea, we had the same problem with retain cycles several times. Despite omitting self in closure currently leads to error, I think passing an object’s method without explicit self should emit warning prior to swift 6, which will become an error in swift 6.