AFAIK there is no way to express a consuming capture for a closure. So when we need to pass a consuming argument we have to use a workaround. For example
func f(_ v: consuming some ~Copyable) {}
func g(_ v: consuming some ~Copyable) { // Error: Missing reinitialization of closure capture 'v' after consume
withExtendedLifetime(()) { // any non-escaping closure
f(v)
}
}
becomes
func f(_ v: consuming some ~Copyable) {}
func g(_ v: consuming some ~Copyable) {
var v = Optional(v)
withExtendedLifetime(()) {
f(v.take()!)
}
}
However, this workaround doesn't compose well with the sending keyword.
func f(_ v: consuming sending some ~Copyable) {}
func g(_ v: consuming sending some ~Copyable) {
var v = Optional(v)
withExtendedLifetime(()) {
f(v.take()!) // Task-isolated value of type 'some ~Copyable' passed as a strongly transferred parameter; later accesses could race;
}
}
I wonder Is there a better workaround here?
Thanks in advance
You're correct - consuming captures don't work and they shouldn't until the language can express something like a call-once closure. The Optional value you're wrapping this in provides a kind of poor runtime-based simulation of the feature you need by pushing the implication of invoking the closure more than once into becoming a runtime crash. AFAIK, what you have is the best we can do until that time.
I agree with you, my personal observation is that consuming and sending does not compose well.
To make your last code snippet work, this is my best effort.
func f(_ v: consuming sending some ~Copyable) {}
func g(_ v: consuming sending some ~Copyable) {
var v = Optional(v)
withExtendedLifetime(()) {
nonisolated(unsafe) let value = v.take()!
f(value)
}
}