Is there a way to specify which scope a deferred block should run at the end of, rather than just the current scope?
e.g. I might want to write:
var data: UnsafeMutablePointer<UInt8>
if someCondition {
data = someNonOwnedResult
} else {
data = someOwnedResult
defer { data.deallocate() } // 🚨 Runs too early.
try … // More work here contingent on !someCondition.
}
try doSomething(with: data)
// *Now* I want to deallocate `data` (if necessary).
I want that defer to run at the end of the outer scope (typically the function's full scope, but not always), but as far as I can tell there's no elegant way to do that today?
Typically I end up with something like:
var data: UnsafeMutablePointer<UInt8>
var deallocateData = false
let condition = someCondition
if condition {
data = someNonOwnedResult
} else {
data = someOwnedResult
deallocateData = true
}
defer {
if deallocateData {
data.deallocate()
}
}
if !condition {
try … // Finish doing all the other !someCondition stuff.
}
try doSomething(with: data)
Even in this contrivedly-trivial case that's pretty unappealing, let-alone in real-world code with multiple levels of indentation (when data
is populated and its deallocation requirement determined) and with much more stuff in-between the key points.
I feel like it'd be quite helpful if defers could be targeted to labels, including an implicit label for the enclosing function, e.g.:
var data: UnsafeMutablePointer<UInt8>
if someCondition {
data = someNonOwnedResult
} else {
data = someOwnedResult
defer to function exit { data.deallocate() }
try … // More work here contingent on !someCondition.
}
try doSomething(with: data)
I know there's other workarounds like making a wrapper type for the object & the flag of whether it should be deleted, and handling that in object deinit, but that's conceptually heavy-weight for such a simple task. (made better, semantically, with non-copyable types so that it can be a struct, but even so)