Non-escaping closure not allowed to do let initialization?


(Shawn Erickson) #1

I attempted the following code which involves passing a closure – one that
doesn't escape obviously – but the compiler complains that I need to make
formHeaders and formStream vars.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        try finish({ (fh, fs, _) in
            formHeaders = fh
            formStream = fs
        })
        return (formHeaders, formStream)
    }

If I have code like this however the use of let is ok.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        if self.finalized.get() {
            formHeaders = ["foo":"bar"]
            //formStream = ...
        }
        else {
            formHeaders = [:]
            //formStream = ...
            // or throw something here
        }
        return (formHeaders, formStream)
    }

If the closure is non-escaping and the code as structured the compiler
should be able to reason about the initialization correctness of those
lets...? ...or am I missing something?

-Shawn


(Jordan Rose) #2

Hi, Shawn. "Non-escaping" doesn't mean "guaranteed to be called at least once", nor "guaranteed to be called at most once". You'd need both of those conditions to make this okay.

There's been some discussion on the swift-evolution list about adding such an annotation, but nothing's been seriously proposed yet. As an additive change to Swift, it isn't something that needs to happen now, just an improvement we could make.

Jordan

···

On Feb 2, 2017, at 12:33, Shawn Erickson via swift-users <swift-users@swift.org> wrote:

I attempted the following code which involves passing a closure – one that doesn't escape obviously – but the compiler complains that I need to make formHeaders and formStream vars.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        try finish({ (fh, fs, _) in
            formHeaders = fh
            formStream = fs
        })
        return (formHeaders, formStream)
    }

If I have code like this however the use of let is ok.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        if self.finalized.get() {
            formHeaders = ["foo":"bar"]
            //formStream = ...
        }
        else {
            formHeaders = [:]
            //formStream = ...
            // or throw something here
        }
        return (formHeaders, formStream)
    }

If the closure is non-escaping and the code as structured the compiler should be able to reason about the initialization correctness of those lets...? ...or am I missing something?

-Shawn
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Shawn Erickson) #3

I meant to state otherwise I have to fallback on IUO to make it work
cleanly...

    public func finish() throws -> ([String:String],InputStream) {
        var formHeaders: [String:String]!
        var formStream: InputStream!
        try finish({ (fh, fs, _) in
            formHeaders = fh
            formStream = fs
        })
        return (formHeaders, formStream)
    }

···

On Thu, Feb 2, 2017 at 12:33 PM Shawn Erickson <shawnce@gmail.com> wrote:

I attempted the following code which involves passing a closure – one that
doesn't escape obviously – but the compiler complains that I need to make
formHeaders and formStream vars.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        try finish({ (fh, fs, _) in
            formHeaders = fh
            formStream = fs
        })
        return (formHeaders, formStream)
    }

If I have code like this however the use of let is ok.

    public func finish() throws -> ([String:String],InputStream) {
        let formHeaders: [String:String]
        let formStream: InputStream
        if self.finalized.get() {
            formHeaders = ["foo":"bar"]
            //formStream = ...
        }
        else {
            formHeaders = [:]
            //formStream = ...
            // or throw something here
        }
        return (formHeaders, formStream)
    }

If the closure is non-escaping and the code as structured the compiler
should be able to reason about the initialization correctness of those
lets...? ...or am I missing something?

-Shawn