iosswiftreactive-cocoareactive-cocoa-4

Understanding a basic Swift compiler error


I understand why this produces a compiler error:

let initialProducer = SignalProducer<Int, NoError>(value:42)
let sideEffectProducer = initialProducer.on(next: { (answer: Int) in
  return _
})

The error is

Cannot convert value of type '(Int) -> _' to expected argument type '(Int -> ())?'

So the next parameter takes a closure with an Int parameter that returns Void whilst we're returning _

But why does this compile fine:

let initialProducer = SignalProducer<Int, NoError>(value:42)
let sideEffectProducer = initialProducer.on(next: { (answer: Int) in
  return ""
})

we're returning a String, not Void so why does the compiler not complain?


Solution

  • _ isn't nothing. It is a pattern, or a part of a pattern that can match anything. It can also be used in an assignment statement to show that you don't care about the result.

    _ = foo()  // Ignore result returned from foo
    

    In your closure, if you want to return nothing, then either:

    return
    

    or omit the return altogether if you're at the end of the closure.

    If you return _, Swift cannot figure out the signature of your closure. You can demonstrate that by doing:

    let bar = { return _ }  // Unable to infer closure return type in current context
    

    If you remove the _, it compiles fine since bar becomes a () -> ().

    Swift could have given you a better error message like it does if you try to return _ from a function:

    func foo() {
        return _  // '_' can only appear in a pattern or on the left side of an assignment
    }
    

    So, why does return "" work? Here's a clue.

    There are some apparent oddness around single-line closures. Consider the following example which is similar to yours:

    func doit(handler: (Int) -> ()) {
        handler(17)
        print("doit is done")
    }
    
    doit() { (answer: Int) in
        //print(answer + 1)
        return ""
    }
    

    Running this produces the output:

    doit is done

    So, like your example doit is expecting a (Int) -> () closure, but we're passing a (Int) -> String closure. And it works...

    But, if you un-comment the print(answer + 1) line, the return "" then results in the error:

    Unexpected non-void return in void function