The enumerateLines function of Swift's String type is declared like this:
enumerateLines(body: (line: String, inout stop: Bool) -> ())
As I understand it, this declaration means: "enumerateLines is a function taking a closure, body, which is handed two variables, line and stop, and returns void."
According to the Swift Programming Language book, I believe I should thus be able to call enumerateLines in a nice terse fashion with a trailing closure, like this:
var someString = "Hello"
someString.enumerateLines()
{
// Do something with the line here
}
..but that results in a compiler error:
Tuple types '(line: String, inout stop: Bool)' and '()' have a different number of elements (2 vs. 0)
So then I try explicitly putting in the arguments, and doing away with the trailing closure:
addressString.enumerateLines((line: String, stop: Bool)
{
// Do something with the line here
})
...but that results in the error:
'(() -> () -> $T2) -> $T3' is not identical to '(line: String.Type, stop: Bool.Type)'
In short, no syntax that I've tried has resulted in anything that will successfully compile.
Could anybody point out the error in my understanding and provide a syntax that will work please? I'm using Xcode 6 Beta 4.
The closure expression syntax has the general form
{ (parameters) -> return type in
statements
}
In this case:
addressString.enumerateLines ({
(line: String, inout stop: Bool) -> () in
println(line)
})
Or, using the trailing closure syntax:
addressString.enumerateLines {
(line: String, inout stop: Bool) in
println(line)
}
Due to automatic type inference, this can be shortened to
addressString.enumerateLines {
line, stop in
println(line)
}
Update for Swift 3:
addressString.enumerateLines { (line, stop) in
print(line)
// Optionally:
if someCondition { stop = true }
}
Or, if you don't need the "stop" parameter:
addressString.enumerateLines { (line, _) in
print(line)
}
Related
My question is derived from the following Japanese question.
It's not my question, but I'm trying to answer the following problem but I cannot find the suitable answer.
https://teratail.com/questions/298998
The question above will be simpled like below.
func executetwice(operation:() -> Void) {
print(operation)
operation()
}
This compiler required to add #escaping keyword after operation: label, such as
func executetwice(operation: #escaping () -> Void) {
print(operation)
operation()
}
But in fact, it seems that operation block does not escape from this block.
Another way,
func executetwice(operation:() -> Void) {
let f = operation as Any
operation()
}
also compiler requires to add #escaping keyword. It is just upcasting to Any.
In other case, just casting to same type, it seems to be error.
func executetwice(operation:() -> Void) {
let f = operation as () -> Void //Converting non-escaping value to '() -> Void' may allow it to escape
operation()
}
I'm not sure why I need to add #escaping keyword with no escaping condition.
Just adding #escaping keyword will be Ok, but I would like to know why the compiler required the keyword in this case.
print accepts (a variable number of) Any as arguments, so that is why it's saying that you are converting a closure to Any when you pass it to print.
Many checks are applied on closure-typed parameters to make sure a non-escaping closure don't escape (for what it means for a closure to "escape", read this):
var c: (() -> Void)?
func f(operation:() -> Void) {
c = operation // compiler can detect that operation escapes here, and produces an error
}
However, these checks are only applied on closure types. If you cast a closure to Any, the closure loses its closure type, and the compiler can't check for whether it escapes or not. Let's suppose the compiler allowed you to cast a non-escaping closure to Any, and you passed it to g below:
var c: Any?
func g(operation: Any) {
// the compiler doesn't know that "operation" is a closure!
// You have successfully made a non-escaping closure escape!
c = operation
}
Therefore, the compiler is designed to be conservative and treats "casting to Any" as "making a closure escape".
But we are sure that print doesn't escape the closure, so we can use withoutActuallyEscaping:
func executetwice(operation:() -> Void) {
withoutActuallyEscaping(operation) {
print($0)
}
operation()
}
Casting a closure to its own type also makes the closure escape. This is because operation as () -> Void is a "rather complex" expression producing a value of type () -> Void. And by "rather complex" I mean it is complex enough that when passing that to a non-escaping parameter, the compiler doesn't bother to check whether what you are casting really is non-escaping, so it assumes that all casts are escaping.
This code compiles and works fine:
class Numbers {
func operateOn<T>(_ num1: T, _ num2: T, do task: (T, T) -> ()) {
task(num1, num2)
}
}
let n = Numbers()
n.operateOn(1,2) {
print(($0 + $1) * 10)
}
n.operateOn("l","ll") {
print(($0 + $1))
}
Yet for for following code does not compile.
func process<T> (add: String, completion: (T) -> () ) {
completion("k") // ERROR
}
Yet I get the following error:
'String' is not convertible to 'T'
I tried passing an Int, but I just got another error:
'Int' is not convertible to 'T'
Can't an Int or a String satisfy a generic requirement that doesn't have any constraints?!
The problem is that your code needs to work for any T. E.g. if T is Int then completion has type (Int) -> (), it's completely legitimate to call
n.process<Int>("") { $0 + 1 }
and completion("k") would have to do "k" + 1 which doesn't make sense.
This is going to be the same in basically any language with generics (C++ is different because it uses templates for the same purpose instead).
Can't an Int or a String satisfy a generic requirement that
doesn't have any constraints?!
Sure it can. But that's not the reason the compiler is giving you an error.
Think about it, what happens if you constrain a generic function/parameter within the function body itself?! It will no longer be a generic function!
Imagine if you had wrote your operateOn function as such:
class Numbers {
func operateOn<T>(_ num1: T, _ num2: T, do task: (T, T) -> ()) {
task("k", num2) // add two strings
}
}
Would you say that T is generic? Or that it's of type String? If you made it a String then can num2 be any generic type it wants to be? It can't!
If it's of type String then it's no longer generic. Since the compiler can't allow that it will throw that error.
I implemented the following function -as an extension of array of booleans- which could throw a CustomError error:
enum CustomError: Error {
case empty
case doesNotContainTrue
}
extension Array where Element == Bool {
func indexOfFirstTrue() throws -> Int {
if isEmpty { throw CustomError.empty }
guard let detectedIndex = index(of: true) else {
throw CustomError.doesNotContainTrue
}
return detectedIndex
}
}
which works as expected:
let myArray = [false, true, false, true]
try print(myArray.indexOfFirstTrue()) // 1
Next, I tried to declare a function as:
func handleResult(_ index: Int) throws {
print(index * 2)
// ...
}
which should take the result of myArray.indexOfFirstTrue() and do something with it (for simplicity, let's assume that it prints the value multiplied by 2):
try handleResult(myArray.indexOfFirstTrue()) // 2
What I want to do is to declare handleResult as rethrowing function:
A function or method can be declared with the rethrows keyword to
indicate that it throws an error only if one of its function
parameters throws an error. These functions and methods are known as
rethrowing functions and rethrowing methods. Rethrowing functions and
methods must have at least one throwing function parameter.
The Swift Programming Language (Swift 4.1): Declarations - Rethrowing Functions and Methods.
So I can call it with non-throwing formula, thus it will not throws an error:
handleResult(myArray.indexOfFirstTrue()) // 2
But I am stuck of what should I edit to let it be a rethrowing function, so I tried to declare it as:
func handleResult(_ index: Int) rethrows {
print(index * 2)
}
and I got the error of:
error: 'rethrows' function must take a throwing function argument
therefore, I also tried to declare it as:
func handleResult(_ index: (() throws -> Int)) rethrows {
print(index * 2)
}
and obviously got the error of:
error: cannot convert value of type 'Int' to expected argument type
'() throws -> Int'
What should I do at this point?
Remember, the argument is of type () -> Int! So you need to call the function passed in to get the result! You also need try since the function can throw.
func handleResult(_ index: (() throws -> Int)) rethrows {
print(try index() * 2) // see the "()"?
}
Now you can use it like this:
let myArray = [true]
try handleResult(myArray.indexOfFirstTrue)
Update at 2016.09.19
There is a tricky, indirect way to use variadic parameters before some other parameters in closure expression parameters list, haha
let testClosure = { (scores: Int...) -> (_ name: String) -> String in
return { name in
return "Happy"
}
}
let k = testClosure(1, 2, 3)("John")
And I found some related issues in bugs.swift.org:
SR-2475
SR-494
Original Post
According to the document of Swift 3.0, for a closure expression, "variadic parameters can be used if you name the variadic parameter"(see Closure Expresssion Syntax part). But for Swift 2.x, the description is "Variadic parameters can be used if you name the variadic parameter and place it last in the parameter list", the border part has been removed in Swift 3.0 document, is it means variadic parameter can be a argument of closure expression even it is not at the last place? If so, why the codes below can't compile successfully?
let testClosure = { (scores: Int..., name: String) -> String in
return "Happy"
}
let k = testClosure(1, 2, 3, "John") // Missing argument for parameter #2 in call
If the argument label can be used in the call, I think the compiler can compile the code above successfully, but in Swift 3.0, closure expression's argument labels are regarded as Extraneous.
Besides, Swift 3.0 document indicates that the parameters in closure expression syntax can be in-out parameters, but Swift 3.0 said that closure expression syntax can use constant parameters, variable parameters, and inout parameters. Why Apple removed descriptions like constant parameters, variable paramters, is it because in Swift 3.0, the parameters can't be var?
Thank you very much for your help!
Still in Swift3 variadic arguments have to be the last parameter in the signature, because despite the fact that in your case the last parameter typed as String can be deduced, there's some cases where not, because of the infinite expansion of variadic argument:
let foo = { (i:Int..., j: Int) -> Int in
return j
}
foo(1,2)
...in Swift 3.0, the parameters can't be var?
var params where removed in Swift3 SE-0003 to avoid confusion with inout parameters, because both var and inout params can be assigned inside function, but just inout is reflected back.
func doSomethingWithVar(var i: Int) {
i = 2 // change visible inside function.
}
func doSomethingWithInout(inout i: Int) {
i = 2 // change reflected back to caller.
}
removing var from parameter list, remove the confusion above.
Variadic parameter have to be last and according to your situation, you can type this:
let testClosure = { (_ name: String, scores: Int...) -> String in
return "Happy"
}
let k = testClosure("John", 1, 2, 3)
You are able to create a func in Swift 3.0 where the variadic parameter is NOT the last argument. For example...
func addButtons(buttons: UIButton..., completion: (() -> ())? = nil)
I believe it's because the parameter following the variadic parameter is named, and so the func does not confuse the next named argument with more variadic arguments.
addButtons(buttons: button1, button2, button3) {
//do completion stuff
}
Swift's documentation on closures states:
Swift’s closure expressions have a clean, clear style, with optimizations that encourage brief, clutter-free syntax in common scenarios. These optimizations include:
Inferring parameter and return value types from context
Implicit returns from single-expression closures
Shorthand argument names
Trailing closure syntax
What exactly is "trailing closure syntax" for Swift closures?
A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.
func doSomething(number:Int, onSuccess closure:(Int)->Void) {
closure(number * number * number)
}
doSomething(number: 100) { (numberCube) in
print(numberCube) // prints 1000000
}
The argument label onSuccess is not there in the function call. Even though the closure is included in the function parameters list, swift will take it out of the parameter block to make the code more readable.
A closure expression that is written outside of (and after) the
parentheses of the function call it supports
It is just syntactic sugar to write less and is easier to read.
Giving you a use case:
You have a function that needs a another function (or closure) as a parameter like that:
func fooFunc(paramfunc: () -> Void) {
paramfunc();
}
Calling the function and giving a function as an argument is normal. Giving it a closure means the argument you give is a nameless function written between {} and with the type signature in the beginning (it is an anonymous function).
While calling a function which needs a function as argument and writing the closure after the parenthesis or omitting the parenthesis if it is the only argument (multiple trailing closures coming in Swift 5.3) makes it trailing closure syntax.
fooFunc { () -> Void in
print("Bar");
}
fooFunc() { () -> Void in
print("Bar");
}
The parenthesis after the function name can even be omitted.
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.
func funcWithATrailingClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
funcWithATrailingClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
funcWithATrailingClosure() {
// trailing closure's body goes here
}
When the last parameter of a function is a closure like this:
class Object {
func foo(bar: String, baz: (() -> Void)) { }
}
You can call it normally like:
let obj = Object()
obj.foo(bar: "", baz: { foo() })
or trailing like this
obj.foo(bar: "") { foo() }
Also, in case of having multiple trailing closure like this:
func foo(bar: String, baz: (() -> Void), pippo: ((IndexPath) -> Void)) { }
then if you try to call it like:
obj.foo(bar: "") { _ in
} pippo: { _ in
}
following error occurs:
Contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored
So prevent that error you need to call like:
obj.foo(bar: "") {
} pippo: { _ in
}
Finally, if you want to use one more closure type like Int
func foo(bar: String, baz: ((Int) -> Void), pippo: ((IndexPath) -> Void)) { }
then call like:
obj.foo(bar: "") { _ in //baz
} pippo: { _ in
}
Trailing Closures
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function. When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.
https://docs.swift.org/swift-book/LanguageGuide/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID102
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
If a closure expression is provided as the function or method’s only argument and you provide that expression as a trailing closure, you do not need to write a pair of parentheses () after the function or method’s name when you call the function:
reversedNames = names.sorted { $0 > $1 }