guard & defer

“We should do (as wise programmers aware of our limitations) our utmost best to … make the correspondence between the program (spread out in text space) and the process (spread out in time) as trivial as possible.” 

Edsger W. Dijkstra“Go To Considered Harmful”

Swift 2.0 introduced two new control statements that aimed to simplify and streamline the programs we write: guard and defer. While the former by its nature makes our code more linear, the latter does the opposite by delaying execution of its contents.

How should we approach these new control statements? How can guard and defer help us clarify the correspondence between the program and the process?

guard

guard is a conditional statement requires an expression to evaluate to true for execution to continue. If the expression is false, the mandatory else clause is executed instead.

func factorial(_ n: Int) throws -> Double throws {
    guard n > 0 else {
        NSExeption.raise()
    }
    return (1...n).map(Double.init).reduce(1.0, *)
}

The else clause in a guard statement must exit the current scope by using return to leave a function, continue or break to get out of a loop, or a function that returns Never like fatalError(_:file:line:).

guard statements are most useful when combined with optional bindings. Any new optional bindings created in a guard statement’s condition are available for the rest of the function or block.

See how optional binding works with a guard-let statement. Compared with if-let statement we can avoid hierarchy of encapsulated expressions. Note: the unwrapped variable t is available even after guard scope has been close.

class Node<T: Comparable> {
    let value: T
    
    var left: Node?
    var right: Node?
    
    init(value: T, left: Node? = nil, right: Node? = nil) {
        self.value = value
        self.left = left
        self.right = right
    }
}

func binaryTreeSearch<T: Comparable>(value: T, tree: Node<T>?) -> Bool {
    guard let t = tree else {
        return false
    }
    
    if t.value == value {
        return true
    } else if value < t.value {
        return binaryTreeSearch(value: value, tree: t.left)
    } else {
        return binaryTreeSearch(value: value, tree: t.right)
    }
    
}

Here before search is started a check is done if the current tree is not nil – mean empty. In that case we immediately stop the search. Otherwise the tree variable is unwrapped and available in the remaining part

defer

Although the defer keyword was already introduced in Swift 2.0, it’s still quite uncommon to use it in projects. Its usage can be hard to understand, but using it can improve your code a lot in some places where you have to cleanup resources.

defer statement is used for executing code just before transferring program control outside of the scope that the defer statement appears in.he most common use case seen around is opening and closing a context within a scope.

If multiple defer statements appear in the same scope, the order they appear is the reverse of the order they’re executed. 

func writeFile() {
    let file: FileHandle? = FileHandle(forReadingAtPath: filepath)
    defer { file?.closeFile() }

    // Write changes to the file
}

tomkausch

Leave a Reply

Your email address will not be published. Required fields are marked *