# Can errdefer and defer be combined?

**URL:** <https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670>\
**Category:** Explain\
**Created:** [April 16, 2025, 9:34pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670 "2025-04-16T21:34:50Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [April 20, 2025, 6:08pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/21 "2025-04-20T18:08:14Z")

</div>

The return is supposed to indicate the end of a function. We don’t want to deviate too far from that by allowing operations of all sorts post return.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [April 20, 2025, 6:48pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/22 "2025-04-20T18:48:36Z")

</div>

> [@chung-leong](#):
>
> Capturing the full retval would allow programmers to do way too much. Can be useful at time for debugging purpose but the potential for misuse is just too great.

I knew that would come up, which is why I said this:

> [@mnemnion](#):
>
> I’m not generally a fan of arguments which boil down to “someone can use this to write bad code”, which shouldn’t be confused with the superficially similar “this feature is hard to use correctly and that will result in bad code too often to justify what we’d get from it”.

Jesus said, “the poor you will always have with you”, and so it is with bad code. A `defer |retval|` block would of course (as written) be a _constant_ capture, and it would be a reasonable guard against misbehavior to forbid a pointer capture here. This is also true:

> [@GigaGrunch](#):
>
> I mean, defer just runs some code at the end of the scope. You do have access to the full retval if you put that code at the end of the scope by hand.

We would get some useful patterns out of it, such as the ability to define postconditions which apply to every return statement in the function, without repetition, and the ability to use those postconditions to swap a return value for an error.

I do think it’s important that this would work on a ‘catch means release’ basis. I think a case could be made that allowing an arbitrary amount of capturing defers and errdefers, rather than, say, one of either per function, would fit the “this feature is hard to use correctly and that will result in bad code too often to justify what we’d get from it” template.

But I would also that such a case would apply equally to the `?E` signature for capturing `defer`. Overall that approach smacks of nerfing: an arbitrary limitation which confuses new users and just becomes a thing you have to memorize, and “why can’t I capture `T` with `defer`” is a question we’ll never get to stop answering.

Zig has very few weird corners like that, and that’s a big part of why I like it so much. I’d like to round down the ones it does have, and not add more. And I’d rather have no capturing defer, than one which concerns itself only with errors and doesn’t let me run postconditional assertions.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [April 22, 2025, 12:07pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/23 "2025-04-22T12:07:08Z")

</div>

At risk of stating the obvious, a programming language should be capable of handling scenarios that programmers may encounter. It does happen that clean-up procedures differ depending on whether an error has happened. The OP provided a real world example. I can easily imagine another:

Suppose we have a function that runs a query on a database. The first thing that it does is acquiring a connection from a pool. Now the next statement depends on the return value. If the rows are returned as a slice of structs, a `defer` statement releasing the connection back to the pool would follow. If an iterator is returned, we would use an `errdefer` instead.

When an error occurs, we might advise the pool to not reuse the connection in the future. A server timeout could mean the connection is no longer usable, for instance. As things stand, we can easily make this distinction only in one version of our function. For no reason at all we’re forced to employ a workaround to handle the exact same scenario just because the function in happier circumstances would return a slice of structs.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [April 22, 2025, 3:03pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/24 "2025-04-22T15:03:34Z")

</div>

I think we may have both run out of interesting things to say to one another on this question. I’ll just point out this:

> [@chung-leong](#):
>
> It does happen that clean-up procedures differ depending on whether an error has happened.

Is of course possible with the signature `E!T`, which is (in a useful sense) a superset of `?E`. I’m aware it solves problems, and which ones, and I think it’s a partial solution where we should prefer a complete one.

Until (unless? I wonder) mere users can open and debate language proposals again, it’s all fairly theoretical. If that does happen, and I hope it will, we can all hash out the details then.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [April 22, 2025, 10:29pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/25 "2025-04-22T22:29:20Z")

</div>

Well, I’m not making a suggestion on how I think the language can be improved. I’m pointing out an actual flaw in the language. An `errdefer` statement might need to capture the error not because it’s somehow an error handler. It needs to capture the error because it’s a type of `defer` statement. It’s just illogical for `defer` itself to be unable to capture the error.

As for capturing the full retval, if you can come up with an usage scenario where you’d need the non-error portion of the error union in order a clean-up, I’d be all ears. I’m pretty sure you can’t. An error set, especially an inferred one, can potentially contain information on a very broad range of concerns. It might tell us that our SQL statement has a syntactical error. It might tell us that our server has spontaneously turned into a whale. When the need to capture the error is rare, the need to capture the right side of the exclamation mark is basically infinitesimally small.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 1, 2025, 4:32pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/26 "2025-05-01T16:32:52Z")

</div>

> [@chung-leong](#):
>
> As for capturing the full retval, if you can come up with an usage scenario where you’d need the non-error portion of the error union in order a clean-up, I’d be all ears. I’m pretty sure you can’t.

```zig
fn blah() !ReturnValue {
    var handle = try magic.getOpenFile();
    defer |ret_err| {
        if (ret_err) |ret| {
            // Can still throw errors
            try handle.close();
            // Because we're returning `ret`
            // After some post-conditions 
            assert(meetsContract(ret));
            return ret;
        else |err| {
            logger.log("some error {}", .{err});
            // Can check if the error
            // allows us to close the handle,
            // or if that would just fail again
            return err;
        }
    }
    // Do stuff with the handle here,
    // It will always be closed
}

```

This only works with a catch-means-release rule, and to have a rule like that, you do have to catch the full `E!T` value.

Kind of bulky written out like that, but easily reduced to an inline function call if that’s desired. Of course half the lines are meta-commentary which a real function would not need.

---

<div class="post-metadata">

**Author:** ![tensorush](https://ziggit.dev/letter_avatar_proxy/v4/letter/t/6a8cbe/32.png) [@tensorush](https://ziggit.dev/u/tensorush)\
**Post date:** [May 1, 2025, 4:45pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/27 "2025-05-01T16:45:15Z")

</div>

Btw, `errdefer` capture feature is getting cut:

> <https://github.com/ziglang/zig/issues/23734>
>
> \## Background
> 
> \`defer\` is a core and incredibly useful component of Zig, which h…elps avoid bugs in resource management. Similarly, \`errdefer\` helps avoid bugs in situations where a resource should be cleaned up only on error conditions. However, there is a third, lesser-known, piece of syntax: \*capturing\* \`errdefer\`. It looks like this:
> 
> \`\`\`zig
> errdefer |err| {
> // \`err\` is the error being returned; you can do stuff with it!
> // Like a normal \`defer/\`errdefer\`, you can't \`return\` or \`try\` in this block, so you can't change
> // which error is returned; but you can e.g. log it
> }
> \`\`\`
> 
> This feature is used \*incredibly\* rarely; many experienced Zig users do not even know that it exists. As a data point, in the Zig repository, ignoring test coverage and documentation for it, there are exactly two uses of this language feature:
> 
> https://github.com/ziglang/zig/blob/8e79fc64cddf6832e4446b20d282084ae69f93ae/tools/update\_cpu\_features.zig#L1529
> 
> https://github.com/ziglang/zig/blob/8e79fc64cddf6832e4446b20d282084ae69f93ae/lib/fuzzer.zig#L298
> 
> A feature being rarely used is not necessarily in itself a reason to remove that feature. However, \`errdefer\` has a bigger design problem.
> 
> \## The Problem
> 
> Here's a question: in \`errdefer |err|\`, what is the \*type\* of \`err\`?
> 
> The obvious thing would be that it has the type of "every error which can be returned after this point in the function", but this isn't a feasible definition; it brings a good amount of (quite boring) complexity to the language specification in terms of how it interacts with things like inferred error sets, and implementing this would require a type of logic in the compiler which likely has compiler performance implications. So, in reality, there are 3 reasonable choices:
> 
> \* The type of \`err\` is the current function's error set.
> \* The type of \`err\` is \`anyerror\`.
> \* The type of \`err\` is the error type which is being returned at a given \`return\` site (so the \`errdefer\` body is reanalyzed at every possible error return with a different type for \`err\`).
> 
> Let's go through these three options. Note that right now, the compiler has inconsistent and unhelpful behavior here, so this \*is\* an unsolved problem.
> 
> The first option is actually fairly good, with two big caveats:
> \* In functions with inferred error sets, \`errdefer |err| switch (err)\` becomes impossible (it would emit a dependency loop because we don't know what cases need to be in the \`switch\`).
> \* It makes something like \`errdefer |err| fatal("error: {}", .{err});\` impossible, since the function needs to return an error for \`err\` to be typed correctly.
> 
> The second option means that any switch on the captured \`err\` must have an \`else\` prong, so if you want to \`switch\` on the captured error, this option is strictly worse than using a wrapper function which \`switch\`es on the returned error (since at this point the error type is known and can be exhaustively switched on). However, people are likely to reach for \`errdefer\` anyway out of convenience, and shoot themselves in the foot by losing type safety.
> 
> The third option is what we \*usually\* do today, but:
> \* It breaks \`switch\` on the captured error, because \`@TypeOf(err)\` will usually only contain a subset of all possible errors, so you'll get errors that \`switch\` prongs are impossible (because e.g. \`error.Foo\` can't be returned at \*this\* particular return site, so \`err\` is \`error{Bar}\` instead).
> \* It makes it significantly harder for any compiler implementation to deduplicate \`errdefer\`s across different error return sites, because they are analyzed differently. (This Zig implementation does not do this deduplication anyway, but it's good for the language spec to make it viable!)
> 
> One observation here is that all three of these solutions have big problems with \`switch\` on the captured error -- and this is a construct we want to \*encourage\*, not discourage! The main other use case for capturing \`errdefer\` is to log errors. The thing is, this use case is actually not a brilliant use for this construct, because it can lead to code bloat (due to the error logging logic being duplicated at every error return site). Generally, the body of an \`errdefer\` should be incredibly simple; it's essentially just intended for resource cleanup. Capturing \`errdefer\` encourages using the construct in more complex ways, which is usually not a good thing! Also, if you're logging an error, this generally indicates you aren't going to explicitly handle the different error cases higher on the call stack, so it's probably desirable to "collapse" these errors down into one (e.g. turn your set of 5 errors into a blanket \`error.FooFailed\`). This is something \`errdefer |err|\` does not support. As such, there are several big advantages to using a "wrapper function" approach instead, like this:
> \`\`\`zig
> pub fn foo() error{FooFailed}!void {
> fooInner() catch |err| {
> std.log.err("error: {s}", .{@errorName(err)});
> return error.FooFailed;
> };
> }
> fn fooInner() !void {
> // ...
> }
> \`\`\`
> 
> So, capturing \`errdefer\` syntax raises a difficult design problem, where all solutions seem unsatisfactory. Furthermore, the common use cases for this feature -- and indeed, the ones it seems to encourage -- tend to emit bloated code and suffer from worse error sets. That leads on to this proposal.
> 
> \## Proposal
> 
> Remove \`errdefer |err|\` syntax from Zig. Uh... yeah, that's kinda it.
> 
> \## Migration
> 
> The two uses in the compiler can be trivially rewritten with these diffs.
> 
> \`lib/fuzzer.zig\`
> \`\`\`diff
> fn traceValue(f: \*Fuzzer, x: usize) void {
> \- errdefer |err| oom(err);
> \- try f.traced\_comparisons.put(gpa, x, {});
> + f.traced\_comparisons.put(gpa, x, {}) catch |err| oom(err);
> }
> \`\`\`
> 
> \`tools/update\_cpu\_features.zig\`
> \`\`\`diff
> fn processOneTarget(job: Job) void {
> \- errdefer |err| std.debug.panic("panic: {s}", .{@errorName(err)});
> + processOneTargetInner(job) catch |err| std.debug.panic("panic: {s}", .{@errorName(err)});
> +}
> +fn processOneTargetInner(job: Job) !void {
> const target = job.target;
> \`\`\`
> 
> These are also basically the two solutions for current uses of this feature: either split your function in two, or perhaps use \`catch\` at the error site(s) if there aren't many. This improves clarity (one less language feature to understand in order to understand your code!), type safety (no issues with \`switch\` exhaustivity, and you can "collapse" error sets where you want as discussed above), and code bloat (no duplication of the error handling path at every possible error return).

I doubt `defer` will be getting a capture. But it’s for the best.

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [May 1, 2025, 6:59pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/28 "2025-05-01T18:59:06Z")

</div>

" Remove `errdefer |err|` syntax from Zig. Uh… yeah, that’s kinda it."  
Ha funny.

I indeed did not know you could capture the error. Which we will not be able to do later on anyway.

It reminds me much of Exceptions (very bad idea they are) in other languages. I never knew (like **really really** know) what to do with them.

I agree that errdefer should _only_ be there for resource cleanup. Although I am aware of the logical difficulties which can arise.

The extreme simplicity of errors in Zig I love. That’s what I always needed.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 1, 2025, 7:16pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/29 "2025-05-01T19:16:30Z")

</div>

> [@pzittlau](#):
>
> The problem I think with attaching a capture to the normal `defer` woulb be, that it could potentially introduce a branch on every single one, if the compiler isn’t that smart.

This shouldn’t be a problem, it’s actually easier than stitching together identical final blocks. The various return points just unconditionally jump to the `defer` logic.

On the other hand, as the issue @tensorush linked to points out, anything which could be done with a capturing defer (or errdefer), can be done with an inner function. Even weird nested stuff, which really should be avoided, could be emulated with multiple levels of nesting, and `inline` can be used to make the result identical for most purposes.

So it’s basically syntax sugar, and not worth complicating the language over. I didn’t care for `errdefer` having a capture semantics, when `defer` didn’t; removing `errdefer |err|` is a satisfying resolution of that inconsistency.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [May 1, 2025, 8:24pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/30 "2025-05-01T20:24:35Z")

</div>

If close() fails, the only reasonable reaction is to either ignore the error or panic. Doesn’t make sense to return an error when the caller cannot do anything in response.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 1, 2025, 9:54pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/31 "2025-05-01T21:54:10Z")

</div>

`file.close()` doesn’t happen to return an error, a better example would be something like `buffered_writer.flush()`, which can.

Code can always catch any error which it doesn’t want to bother the caller with. Current `defer` can’t return, so a fallible function call in a `defer` block would be obliged to do this.

But really, something [like this](https://codeberg.org/atman/zg/src/commit/10048b0d31d0db923ae39c6bbd67139ed6252f6f/src/GeneralCategories.zig#L49) gets almost all the benefits, without adding complexity to the language. As I said, removing capturing `errdefer` eliminates the idiosyncrasy with less effort and complexity, which is the way to go.

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [May 1, 2025, 10:34pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/32 "2025-05-01T22:34:25Z")

</div>

If `OutOfMemory` is the only possible error, wouldn’t it be better to remove the else branch so that it becomes a compile error if any other error gets added to the inferred error set?

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 2, 2025, 12:44am UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/33 "2025-05-02T00:44:50Z")

</div>

Oh wait. Looks like I circled back and doubled up on something there. I converted two of the setup function (that and `Graphemes` I believe) to use individual `catch unreachable` branches for each error, but that was getting tedious, so I started doing it [like this](https://codeberg.org/atman/zg/src/branch/v0.14-beta/src/Properties.zig#L18) (different link, same trick).

Looks like what happened is that I went back to `GeneralCategories` and set up the error filter for it even though it doesn’t strictly need it. Easy enough to change, it’s a beta after all.

I think the basic pattern here is sound however. Any error other than `OutOfMemory` would indicate a bug in either `zg` or `std`, which I would need to fix somehow. There’s no point in user code doing the filtering for those, and by the contract of the operation, allocation failure is the only error which makes sense to expose. So if stdlib split one of those errors into two new errors, I would want the `else` branch to get rid of the new one automatically.

It’s a good general point, only use an `else` branch if the contract stipulates that any new members would go into the `else` branch. In this case it does. These functions decompress a segment of memory into a data structure, the only syscalls are from the allocator and it’s otherwise deterministic.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 2, 2025, 12:47am UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/34 "2025-05-02T00:47:54Z")

</div>

> [@mnemnion](#):
>
> Looks like what happened is that I went back to `GeneralCategories` and set up the error filter for it even though it doesn’t strictly need it.

I had missed a `try`while I was going through setting up those filters, so I hadn’t noticed that I had almost finished doing it the hard way.

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [May 2, 2025, 10:39am UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/35 "2025-05-02T10:39:54Z")

</div>

Your own example demonstrates why what you’re pushing for is ill-advised. The following is a resource leak:

```zig
    defer |ret| {
        try buffered_writer.flush();
        handle.close();
        return ret;
    }

```

Programmers cannot make this mistake currently.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [May 2, 2025, 4:28pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/36 "2025-05-02T16:28:56Z")

</div>

That looks more like your example than anything I wrote. Considering you wrote that, calling it “my example” is some sort of rhetorical move, no?

I wonder what you’re getting out of all this dead horse beating.

> [@chung-leong](#):
>
> Programmers cannot make this mistake currently.

This is of course false:

```zig
try buffered_writer.flush();
handle.close();
return ret;

```

Same resource leak. The existence of `defer` does not compel the use of it, and it is also the case that `defer` statements can have bugs.

But hey, here’s “my” example:

```zig
    defer |ret| {
        defer handle.close();
        try buffered_writer.flush();
        return ret;
    }

```

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [May 2, 2025, 4:42pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/37 "2025-05-02T16:42:36Z")

</div>

`try` can’t be used within a defer (or `return`) so the whole discussion doesn’t make sense. And @tensorush has already shown that we will get less captures, not more.

```zig
    defer {
        const val = try getSwitched(4);
        std.debug.print("val {}", .{val});
    }

```

```zig
temp24.zig:26:21: error: 'try' not allowed inside defer expression
        const val = try getSwitched(4);
                    ^ ~~~~~~~~~~~~~~~~~
temp24.zig:25:5: note: defer expression here
    defer {
    ^ ~~~~

```

---

<div class="post-metadata">

**Author:** ![chung-leong](https://ziggit.dev/user_avatar/ziggit.dev/chung-leong/32/999_2.png) [@chung-leong](https://ziggit.dev/u/chung-leong)\
**Post date:** [May 3, 2025, 9:25am UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/38 "2025-05-03T09:25:53Z")

</div>

This discussion is completely off the rail. Somehow we’ve gotten further from a solution to the OP’s problem than before. Without capture in `errdefer` means that if your code needs the error you’re have to catch it yourself, in every single instance. We go from this:

```zig
    try doA();
    try doB();
    try doC(try doD(), try doE(), try doF());

```

To this:

```zig
    var last_error_maybe: ?anyerror = null;
    doA() catch |err| {
        last_error_maybe = err;
        return err;
    };
    doB() catch |err| {
        last_error_maybe = err;
        return err;
    };
    doC(doD() catch |err| {
        last_error_maybe = err;
        return err;
    }, doE() catch |err| {
        last_error_maybe = err;
        return err;
    }, try doF() catch |err| {
        last_error_maybe = err;
        return err;
    }) catch |err| {
        last_error_maybe = err;
        return err;
    };

```

Why are we forcing people to write this crazy stuff? Just let `defer/errdefer` statement see the error. In other systems, it’s not uncommon to expose the last error globally. In C/POSIX we have `errno`. In Windows we have `GetLastError()`. In PHP, `error_get_last()`. The list goes on.

---

<div class="post-metadata">

**Author:** ![phatchman](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/e79b87/32.png) [@phatchman](https://ziggit.dev/u/phatchman)\
**Post date:** [May 3, 2025, 11:57am UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/39 "2025-05-03T11:57:06Z")

</div>

> [@chung-leong](#):
>
> ```zig
> var last_error_maybe: ?anyerror = null;
> doA() catch |err| {
> last_error_maybe = err;
> return err;
> };
> doB() catch |err| {
> last_error_maybe = err;
> return err;
> };
> doC(doD() catch |err| {
> last_error_maybe = err;
> return err;
> }, doE() catch |err| {
> last_error_maybe = err;
> return err;
> }, try doF() catch |err| {
> last_error_maybe = err;
> return err;
> }) catch |err| {
> last_error_maybe = err;
> return err;
> };
> 
> ```

Wouldn’t your example be solved by putting all of those calls in a separate function and doing a single catch on that function?

If you must do it as a single function, the best I’ve come up with is below.

```zig
    const last_error_maybe: ?anyerror = err: {
        doA() catch |err| break :err err;
        doB() catch |err| break :err err;
        break :err null;
    };
    if (last_error_maybe) |err| {
        std.debug.print("err: {s}\n", .{@errorName(err)});
    }

```

---

<div class="post-metadata">

**Author:** ![milogreg](https://ziggit.dev/letter_avatar_proxy/v4/letter/m/7cd45c/32.png) [@milogreg](https://ziggit.dev/u/milogreg)\
**Post date:** [May 3, 2025, 7:58pm UTC](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670/40 "2025-05-03T19:58:34Z")

</div>

I agree that this is cumbersome, but it’s somewhat unrelated to OP’s specific goal: to conditionally execute based on whether an error exists, not based on what the error is.

OP’s problem could be solved without `errdefer` capture or any additional boilerplate for error-returning calls:

```zig

{
    // Related to DBus message iterators
    var iter: Message.Iter = undefined;
    parent.openContainer(&iter);

    // Abandon on error
    errdefer parent.abandonContainer(&iter);

    // But close only if it doesn't error
    var returned_err = false;
    defer if (!returned_err) parent.closeContainer(&iter);
    errdefer returned_err = true;

    // Do other things with iter
}

```

[Previous page](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670.md?page=1)

[Next page](https://ziggit.dev/t/can-errdefer-and-defer-be-combined/9670.md?page=3)
