Errdefer free() + manual free() before the end of the function

Let’s say need a temporary memory block in my function, which I allocate dynamically. Naturally, I would like it to be freed, should my function return an error - this is what errdefer is for.
But I also want to free this block before my function ends, and the function MAY return error after that, which would trigger errdefer.

Here’s my test program:

const std = @import("std");

const MyError = error { AnError };

pub fn main() void
{
    ham() catch { std.debug.print("Error\n", .{}); };
}

fn ham() !void
{
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc8r = gpa.allocator();

    var sl = try alloc8r.alloc(u8, 12);
    errdefer alloc8r.free(sl);

    alloc8r.free(sl);

    return MyError.AnError;
}

Naturaly, it coredumps at errdefer alloc8r.free(sl);. If i comment it out, it works as expected.

So my solution is to

errdefer if (sl.len > 0) alloc8r.free(sl);

And

alloc8r.free(sl);
sl.len = 0;

But I wonder if there are better ways. Are there?

defer

fn ham(allocator: std.mem.Allocator) !void {
    var sl = try allocator.alloc(u8, 12);
    defer allocator.free(sl);

    // ...

    return MyError.AnError;
}

(defers still run if the function returns an error)


If you want the allocation to only live for a certain portion of the function, then you can use defer in a block like so:

fn ham(allocator: std.mem.Allocator) !void {
    // stuff

    {
        var sl = try allocator.alloc(u8, 12);
        defer alocator.free(sl);

        // ...

        // sl will be freed at the end of this block
    }

    // stuff
}
9 Likes

The second pattern (code block + defer) is what I want, thank you!

I keep forgetting that defered code is called when returning errors as well as returning normally.