[swift-evolution] Pitch: only allow capture of inout parameters in @noescape closures
Chris Lattner
clattner at apple.com
Thu Jan 28 21:48:38 CST 2016
> On Jan 28, 2016, at 1:50 PM, Jordan Rose via swift-evolution <swift-evolution at swift.org> wrote:
>
> +1 from me, with no new capture kind.
Me too: +1 from me, with no new capture kind.
-Chris
> I think the workaround is easy enough that we don't need to worry about APIs that haven't been annotated yet, or APIs that are synchronous this time. Of course, it would be nice if the compiler could even insert the copy/writeback in a note fix-it.
>
> Jordan
>
>> On Jan 28, 2016, at 11:58, Joe Groff via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>
>> I think the time has come for us to limit the implicit capture of 'inout' parameters to @noescape closures. In the early days before @noescape, we designed a semantics for capturing "inout" parameters that attempted to balance the constraints on inout with the usability of higher-order functions. Inout parameters don't pass a first-class reference as in other languages, but instead provide a limited lease to mutate a property for the duration of a call; this means closures can't extend the lifetime of that lease, so closures instead capture the local mutable copy of the inout parameter. This gives the expected behavior as long as closures over an inout parameter never escape their original scope, but leads to surprises as soon as those closures escape. This unintuitive behavior has made several "Swift gotchas" lists. Now that we can explicitly mark closures as not escaping, I think we should prevent 'inout' parameters from being implicitly captured by escapable closures. There are several ways we can still support today's behavior:
>>
>> - Encourage people to write the local copy themselves, if that's what they want:
>>
>> func foo(inout x: Int) {
>> var localX = x; defer { x = localX }
>>
>> // Asynchronously mutate localX, then wait for it to finish
>> dispatch_async { mutate(&localX) }
>> dispatch_sync {}
>> }
>>
>> - You can explicitly capture an immutable copy:
>>
>> func foo(inout x: Int) {
>> // We don't need to mutate or observe mutations on x in the async job
>> dispatch_async {[x] in doStuffWithoutMutating(x) }
>> }
>>
>> and/or we could introduce a new explicit capture kind for the current behavior:
>>
>> func foo(inout x: Int) {
>> // Explicitly capture the 'inout' mutable shadow copy of x
>> dispatch_async {[inout x] in mutate(&x) }
>> dispatch_sync { }
>> }
>>
>> Making it explicit should make the behavior less surprising when it occurs. We should able to provide fixits to migrate code that relies on the current behavior as well. What do you all think?
>>
>> -Joe
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160128/f91f9298/attachment.html>
More information about the swift-evolution
mailing list