How to use Swift @autoclosure
Consider a function that takes one argument, a simple closure that takes no argument:
func f(pred: () -> Bool) {
if pred() {
print("It's true")
}
}
To call this function, we have to pass in a closure
f(pred: {2 > 1})
// "It's true"
If we omit the braces, we are passing in an expression and that's an error:
f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'
@autoclosure
creates an automatic closure around the expression. So when the caller writes an expression like 2 > 1
, it's automatically wrapped into a closure to become {2 > 1}
before it is passed to f
. So if we apply this to the function f
:
func f(pred: @autoclosure () -> Bool) {
if pred() {
print("It's true")
}
}
f(pred: 2 > 1)
// It's true
So it works with just an expression without the need to wrap it in a closure.
Here's a practical example — my print
override (this is Swift 3):
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
#if DEBUG
Swift.print(item(), separator:separator, terminator: terminator)
#endif
}
When you say print(myExpensiveFunction())
, my print
override overshadows Swift's print
and is called. myExpensiveFunction()
is thus wrapped in a closure and not evaluated. If we're in Release mode, it will never be evaluated, because item()
won't be called. Thus we have a version of print
that doesn't evaluate its arguments in Release mode.