Swift: Defer

I kinda start to love Swift.
Even if at some moments I clearly hate it (when they removed the pre/post de/incrementation for instance), I feel like discovering a new world as I don’t feel as comfortable as I feel with C.

And I like when I discover some new syntax designed to smooth/simplify things out.

For instance, in C, we are used to ‘nesting ifs’ when we need a function to leave at any moment in the process while needing to release/clean stuff before leaving. It is also one good example where the ‘goto’ is useful and needed.

Consider this, in a “nested ifs” flavor:

Image *getImageFromFile(const char *filename) {
    Image *image = NULL;

    if (fileExists(filename)) {
        image = malloc(sizeof(Image));
        if (image) {
            FILE *fp = fopen(filename, "r");
            if (fp) {
                // ...
                fclose(fp);
            } else {
                free(image);
            }
        }
    }

    return image;
}

or in a “goto” flavor:

Image *getImageFromFile(const char *filename) {
    Image *image;
    FILE *fp;

    if (fileExists(filename) == false) {
        goto readError;
    }
    image = malloc(sizeof(Image));
    if (image == NULL) {
        goto readError;
    }

    fp = fopen(filename, "r");
    if (fp == NULL) {
        goto readError;
    }

    // ...
    
    fclose(fp);
    free(image);

    return image;

readError:
    if (fp) fclose(fp);
    free(image);

    return NULL;
}

to give a rough example.

People prefer one or the other, it’s up to you. But the principle is: you enter different level depending on the state of the previous one, so you need to clean things depending on where you want to exit.

And in Swift, we do have DEFER!!

Defer allows you to declare a block of code to be executed once the main scope leaves. And there can be multiple of them, stacking in a L.I.F.O. manner! How neat!

So for instance, it could be done this way:

func getImage(filename: String) -> Image? {
    let myFile = openFile(name: filename)
    defer { CloseFile(myFile) }
    if myFile == nil { return nil }

    let rect: (x: Int, y: Int) = getImageRect(fromFile: myFile!)
    if rect.x * rect.y < 1 { return nil }

    let pixels: Data? = alloc(rect.x * rect.y * 3)
    defer { pixels.dealloc(rect.x * rect.y * 3) }
    if pixels == nil { return nil }

    let image: Image? = getImage(fromPixels: pixels!)

    return image
}

This way the defer will stack to ‘close the file’, ‘deallocate the buffer’ depending on what it reached.
And when the function leaves these get executed in reverse order.