[swift-evolution] Pitch: only allow capture of inout parameters in @noescape closures

Dave Abrahams dabrahams at apple.com
Thu Jan 28 16:19:47 CST 2016

on Thu Jan 28 2016, Joe Groff <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 {}
> }


As the author of the current semantics, I can say with confidence that
if we'd had @noescape at our disposal originally we would never have
done this.

> - 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 { }
> }

-1.  Let's not complicate the language for this rare case.

> 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
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list