5

Consider the following set of functions:

func testFunc(someFunc: (Int[]) -> ()) {
    someFunc([1, 2, 3])
}

func someFunc<T>(arr : T[]) -> T[] {
    return arr
}

func someOtherFunc<T>(arr : T[]) {
    println(arr)
}

// case 1 - ERROR
testFunc() {
    someFunc($0)
}

// case 2 - no error
testFunc() {
    println("whatever")
    someFunc($0)
}

// case 3 - no error
testFunc() {
    someOtherFunc($0)
}

It looks like in case 1, Swift is trying to implicitly return from the closure since the function someFunc() returns a value. It only does this if there is only one line in the closure (Implicit Returns from Single-Expression Closures) - this is why case 2 works. It does not do this if the function, as in case 3 is Void, i.e. it doesn't return a value.

My question is whether there is a way to suppress this behavior so that I can have a function with a return value as a single-line expression in a closure that has no return value.

Cezary Wojcik
  • 21,745
  • 6
  • 36
  • 36
  • 1
    "someFunc($0); return" ? Whats wrong with two lines – sanz Jun 05 '14 at 21:40
  • 5
    This looks like a bug. (@tony there is no reason why you should have to add additional lines for this case.) Type inference should catch the fact that `testFunc` takes a closure which returns nothing `-> ()` and should appropriately suppress the automatic return of the single-expression closure. Please report it to https://bugreport.apple.com/ – Nicholas H. Jun 05 '14 at 21:44
  • A bug makes more sense... I thought I was going crazy. Bug report submitted, thanks! – Cezary Wojcik Jun 05 '14 at 21:52
  • 1
    Just ran into this as well, looks more like a bug to me – Pride Chung Jun 08 '14 at 13:13
  • @NicholasH.: Why do you think it is a "bug"? It is doing exactly as the specification says it would. – newacct Aug 26 '14 at 22:07
  • "It does not do this if the function, as in case 3 is Void" Yes it does. It returns the result of that line, which is `()`, of type `()` (a.k.a. `Void`), which is the return type of the function. – newacct Aug 26 '14 at 22:08

2 Answers2

7

Besides the mentioned solutions:

testFunc { someFunc($0); return () } // returning Void explicitly (with or without parenthesis)

testFunc { someFunc($0); 42 } // or, indeed, just adding a second expression

You can also consume the returned value:

testFunc { let x = someFunc($0) }

or simply:

testFunc { _ = someFunc($0) }

Returned value must always be of the type promised by the function signature, and the case of the implicit return is no different. This is no bug. It is simply that the implicit returns are so often such elegant syntax that the less frequent case of the mismatching types is breaking the spell a bit. This is not to say that a nice syntactic solution would not be welcomed, at least when Void is expected. Perhaps something as simple as:

testFunc { someFunc($0) ; } // with the trailing semicolon

When this irritates me the most is when my own function forces me to dance around it. I have a couple of times resorted to explicitly ignoring the return type:

func testFunc<Ignored>(someFunc: [Int] -> Ignored) {
    someFunc([1, 2, 3])
}

testFunc { someFunc($0) }
Milos
  • 2,728
  • 22
  • 24
1

UPDATE: After Swift 1.2, this is no longer a problem

This bug still exists in Xcode beta6, I hope it will be fixed in the 1.0 release, before that, this is a workaround

testFunc() {
    someFunc($0)

    // The explicit return statement corrects the wrong return type of the auto implicit return.
    // It makes more sense than printing nonsense strings
    return //TODO: remove after the bug is fixed
}
Pride Chung
  • 573
  • 7
  • 20
  • Why do you think it is a "bug"? It is doing exactly as the specification says it would. – newacct Aug 26 '14 at 22:07
  • @newacct read NicholasH's comment under the question, I agree with him. – Pride Chung Aug 27 '14 at 08:16
  • @newacct One more source, on GitHub there's a similar issue caused by implicit returns, someone said the Apple guys have confirmed it's a compiler bug https://github.com/robb/Cartography/issues/9 – Pride Chung Aug 27 '14 at 08:28
  • If the body of a closure is a single expression, it is implicitly returned. Always. That's what's happening. – newacct Aug 27 '14 at 09:06
  • 1
    @newacct I don't know where you get that idea, at least I can't find it from Apple's Swift book. The book only states `there is no ambiguity, and the return keyword can be omitted.` In the question's case apparently the compiler made a mistake on the return type inference, the outter function `testFunc` returns Void, and yet the compiler still implicit returns the closure's return value, this is nonsense and confusing. If Apple do not consider it's a bug, then I would call it a trap. – Pride Chung Aug 27 '14 at 14:46
  • Can you point to any example in which a single expression closure *does not* implicitly return that expression? The Apple documentation says "Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration" without specifying any conditions on it. Later on there is discussion about how `return` can be omitted in the specific example given; but it does not say `return` is not implicit in other situations. – newacct Aug 27 '14 at 18:09