Hello, everyone!
Today I’d like to pitch a proposal that could radically improve the modeling power of Swift.
Any and all feedback is very welcome!
Rationale
The ergonomics of captured references has been brought up numerous times, so I’d like to begin a discussion about a more universal solution to this entire domain of problems with a pitch to allow delegating of of reference capture semantics to outside the scope where it is captured.
If implemented, this feature will, among many other use cases, solve the reoccurring problem of closure capture syntax, as well as improve the widely used delegate pattern, decoupling it from class semantics.
Details
Structures
A weak structure is a property marked with the keyword weak
of type T?
(where T
is a declared as a struct
):
struct MyStruct {
let myClass: MyClass
let myClosure: () -> Void
}
weak var myStruct: MyStruct?
Such a property actually holds a hidden compiler-generated structure, where all its fields are declared as weak
:
struct __Weak_MyStruct {
weak var myClass: MyClass?
weak var myClosure: (() -> Void)?
mutating func unwrapping<Result>(into body: (inout MyStruct) throws -> Result) rethrows -> Result? {
guard let myClass = self.myClass, let myClosure = self.myClosure else {
return nil
}
var myStruct unsafeBitCast((myClass, myClosure), to: MyStruct.self)
defer {
self = .init(myClass: myStruct.myClass, myClosure: myStruct.myClosure)
}
return try body($myStruct)
}
}
var myStruct = __Weak_MyStruct()
When accessing the property, all properties of the hidden structure are unwrapped. If all of them unwrapped successfully, then they’re arranged into the original structure and returned as such, otherwise, `nil is returned:
myStruct?.myClosure()
// translates to
myStruct.unwrapping { $0.myClosure() }
Enums
Since an enum case is essentially a structure containing an opaque discriminator and a memory region used for storing a tuple (where cases without associated objects correspond to ()
), the rules of weak/unowned enums are identical to the rules of weak/unowned structures (see above).
Tuples
Since tuples are essentially anonymous structures, the rules of weak/unowned tuples are identical to the rules of weak/unowned structures (see above).
Closures
Since closures are essentially structures with stored properties holding captured variables and a single method implementing the closure body, the rules of weak/unowned closures are identical to the rules of weak/unowned structures (see above).
Any
Since an existential container is essentially a structure with an opaque payload, metadata and a witness table, the aforementioned unwrapping(into:)
method (or a type-erased version thereof) can be included into all witness tables).