[swift-evolution] [Pitch] Limit Implicit Capture

Daniel Duan daniel at duan.org
Sun Mar 13 21:12:31 CDT 2016


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?


More information about the swift-evolution mailing list