Skip to content
FutureBlitz

    Install

    Errors, 3 of 4

    Finally and Defer

    Run cleanup whether a block succeeds or fails, with a Try's Finally or with Defer.

    What you’ll build

    A job runner that starts each job, runs it, and always cleans up after it, whether the job succeeds, fails, or returns early. It does the cleanup two ways, with Finally and with Defer, and shows when to use each.

    Finally

    A Try block’s Finally runs however the Try ends: after the body succeeds, after a Catch handles an error, and when the body leaves through Return, Break, or an error that no Catch takes.

    jobs.fb
    Import std.core
    
    Function Run(name: String, fail: Bool): String Throws Error
        If fail Then Throw Error("{name} failed")
        Return "{name} done"
    End Function
    
    For fail In [False, True]
        Try
            Print(Run("build", fail))
        Catch e
            Print("caught: {e.Message}")
        Finally
            Print("finally, after {fail ? "a failure" : "a success"}")
        End Try
    End For
    Output
    build done
    finally, after a success
    caught: build failed
    finally, after a failure

    A Finally can’t throw, or leave with Return, Break, or Continue, so it always finishes and lets the Try end as it was going to.

    Defer runs on errors too

    A Defer runs when its block ends, and that includes ending with an error. The cleanup sits right after the setup it undoes, and the Try that handles the error can be in another function entirely.

    jobs.fb
    Import std.core
    
    Function Run(name: String, fail: Bool): String Throws Error
        Print("start {name}")
        Defer Print("clean up {name}")
        If fail Then Throw Error("{name} failed")
        Return "{name} done"
    End Function
    
    For fail In [False, True]
        Try
            Print(Run("test", fail))
        Catch e
            Print("caught: {e.Message}")
        End Try
    End For
    Output
    start test
    clean up test
    test done
    start test
    clean up test
    caught: test failed

    The cleanup runs before the error reaches the Catch.

    A Defer that could fail

    A deferred statement can’t throw, since nothing would be there to catch its error. A cleanup call that can fail uses Try?, which drops its error.

    jobs.fb
    Import std.core
    
    Function Release(name: String) Throws Error
        If name == "" Then Throw Error("nothing to release")
        Print("released {name}")
    End Function
    
    Function Work(name: String)
        Defer Try? Release(name)
        Print("working on '{name}'")
    End Function
    
    Work("cache")
    Work("")
    Output
    working on 'cache'
    released cache
    working on ''

    Which to use

    Use Defer to undo something right where it’s set up: close what was opened, release what was taken. It keeps the two together, and it runs whether or not a Try is around. Use Finally when the cleanup belongs to the handling of the error itself, alongside the Catch.

    jobs.fb
    Import std.core
    
    Global running = 0
    
    Function Run(name: String, fail: Bool) Throws Error
        running++
        Defer running--
        If fail Then Throw Error("{name} failed")
        Print("{name} ran, with {running} running")
    End Function
    
    Local failures = 0
    For job In ["lint", "build", "deploy"]
        Try
            Run(job, job == "build")
        Catch e
            failures++
        Finally
            Print("after {job}: {running} running, {failures} failed")
        End Try
    End For
    Output
    lint ran, with 1 running
    after lint: 0 running, 0 failed
    after build: 0 running, 1 failed
    deploy ran, with 1 running
    after deploy: 0 running, 1 failed

    What you learned

    • Finally runs however a Try ends: success, a caught error, Return, or an error passing through.
    • Defer runs when its block ends, through an error too, before a Catch further out.
    • A deferred call that can throw is written with Try?.
    • Defer keeps cleanup next to its setup; Finally keeps it with the error handling.

    Report an issue with this page or with FutureBlitz on GitHub.