[swift-evolution] [Pitch] Limit Implicit Capture

Haravikk swift-evolution at haravikk.me
Mon Mar 14 05:56:54 CDT 2016


I think I’m in favour of something along these lines, though personally I think the better solution is to simply eliminate implicit capture for closures and local functions, and instead provide a @capture attribute to re-enable current behaviour (and to avoid breaking code). This way developers are encouraged to either opt-in to the implicit capture behaviour, or declare a capture list, with the latter being preferred as it can more clearly declare what you need and in what capacity.

That said I think that do { … } blocks should retain implicit capture, as they’re really just a sub-section of your code, even if in some ways they can be thought of as a closure that automatically executes.

> On 14 Mar 2016, at 02:12, Daniel Duan via swift-evolution <swift-evolution at swift.org> wrote:
> 
> I'm curious to see if anyone else has desire for this change.
> 
> Currently, scopes created by functions, closures, "do {}", etc.
> implicitly capture values from their outer scopes. The only way to opt out
> this behavior is via functions defined "elsewhere":
> 
> func a() { ... }
> func foo(b: () -> ()) {
>    func c() { ... }
>    let d = { ... }
> 
>    a() // nothing from foo's scope will implicitly get into a
>    b() // nothing from foo's scope will implicitly get into b
> 
>    c() // implicitly captures values in foo
>    d() // implicitly captures values in foo
>    do {
>        // implicitly captures values in foo
>    }
> }
> 
> One problem that comes with this bebavior is unintended capturing. E.g. a user
> may think they successfuly factored out some code, but a missing variable was
> satified by something with the same name from an outer scope.
> 
> C++ addresses this issue by making its user explicitly indicate lambda's
> capturing behavior:
> 
> [] {...}  // capture nothing
> [=] {...}  // capture everything by value
> [&] {...}  // capture everything by reference
> 
> It'd be nice if Swift can allow user to opt out the automatic capturing at
> some level. We already have the capture list syntax, reusing it for explictly
> capture in this case:
> 
> func foo() {
>    let a = 5
>    let b = "Ziggy"
>    let c = ["Weild", "Gilly"]
> 
>    let d: @explicit_capture () -> () = { [a, b] in
>        let years = a // ok
>        let artist = b // ok
>        let others = c // error: implicit capture in not allowed for 'd'
>    }
> }
> 
> 
> An alternative would be making implicit capture an opt-in feature (similar to
> C++):
> 
> func foo() {
>    let a = 5
>    let b = "Ziggy"
>    let c = ["Weild", "Gilly"]
> 
>    let d = { [a] in
>        let years = a // ok
>        let artist = b // error: implicit capture in not allowed for 'd'
>        let others = c // error: implicit capture in not allowed for 'd'
>    }
> 
>    let e: @capture_all () -> () = { [a] in
>        let years = a // ok
>        let artist = b // ok
>        let others = c // error: implicit capture in not allowed for 'e'
>    }
> }
> 
> Obviously, this version would be a breaking change.
> 
> I have no attchment to the syntax. Chris has brought up moving @noescape
> before variable types declaration, so putting @explicit_capture there seems
> natural.
> 
> Thoughts?
> _______________________________________________
> 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