# Idiom for an "old-school" try-catch block

**URL:** <https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821>\
**Category:** Help\
**Tags:** language\
**Created:** [April 4, 2024, 1:48am UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821 "2024-04-04T01:48:33Z")\
**Posts on this page:** 7\
**Page:** 2

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [July 20, 2026, 1:53pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/21 "2026-07-20T13:53:36Z")

</div>

the “fix” is to move the `errdefer` code into one of the `catch` statements in the block (or to move the block to a function)

* * *

@dhw9406 the point is that the `errdefer` _is_ tied to block scope, but the trigger condition for all `errdefer` statements is “`return err`”, which `break :blk err` is technically not.

---

<div class="post-metadata">

**Author:** ![dhw9406](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/90db22/32.png) [@dhw9406](https://ziggit.dev/u/dhw9406)\
**Post date:** [July 20, 2026, 2:10pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/22 "2026-07-20T14:10:29Z")

</div>

If the trigger condition for defer is block scope exit, but the trigger condition for errdefer is function returns an error, this seems inconsistent to me. Thinking about it further, since blocks have ‘output values’ (not sure what the right verbiage is here), and block scope exit is used (by defer, break, etc) for control flow, it would not be inconsistent to say that errdefer should fire on block scope exit if the result value of the block is an error.

One counter argument, I suppose, would be that one may not want that error clean up code to run that early, but in that case the errdefer could just be moved to right after the block.

---

<div class="post-metadata">

**Author:** ![dhw9406](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/90db22/32.png) [@dhw9406](https://ziggit.dev/u/dhw9406)\
**Post date:** [July 20, 2026, 2:16pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/23 "2026-07-20T14:16:39Z")

</div>

Or perhaps I am misunderstanding something: given the different trigger conditions, exactly how is errdefer tied to block scope?

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [July 20, 2026, 2:59pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/24 "2026-07-20T14:59:17Z")

</div>

`errdefer` either: at normal block scope exit, nothing happens. if instead block scope is exited by `return err`, it should run

---

<div class="post-metadata">

**Author:** ![dhw9406](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/90db22/32.png) [@dhw9406](https://ziggit.dev/u/dhw9406)\
**Post date:** [July 20, 2026, 4:07pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/25 "2026-07-20T16:07:38Z")

</div>

One might equally write:

`errdefer` either: at normal function scope exit, nothing happens. if instead function scope is exited by `return err` , it should run

And this second version still correctly describes the current behavior. Which, to me, says that errdefer is in fact tied to function scope, because the only way for it to run on block scope exit is through a function return. And this is surprising to me. Not necessarily incorrect, but maybe incorrect.

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [July 20, 2026, 5:34pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/26 "2026-07-20T17:34:31Z")

</div>

one _could_ write this, true! as you know, it’s a testable assertion that you are making, so let’s test it.

```zig
const std = @import("std");

pub fn main() !void {
    try foo(false); // replace with true if you'd like
}

fn foo(in: bool) !void {
    {
        errdefer std.debug.print("errdefer!\n", .{});
        if (in) return error.Foo;
    }
    return error.Bar;
}

```

in accordance with what _I_ wrote, this program does _not_ print `errdefer!` when run as written, but it _does_ do so if you replace `try foo(false);` with `try foo(true);`

---

<div class="post-metadata">

**Author:** ![dhw9406](https://ziggit.dev/letter_avatar_proxy/v4/letter/d/90db22/32.png) [@dhw9406](https://ziggit.dev/u/dhw9406)\
**Post date:** [July 20, 2026, 5:42pm UTC](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821/27 "2026-07-20T17:42:50Z")

</div>

The way in which errdefer is scoped is clear to me now; thanks for the code! Code is usually the best way to communicate precisely. I still think that it’s a little surprising that an errdefer in a block does not fire when that block results in an error, but until I’m off my phone and at my computer to write some experiments in code, I can’t really see which cases argue for one semantic over the other. This has been enlightening, though, so thanks!

[Previous page](https://ziggit.dev/t/idiom-for-an-old-school-try-catch-block/3821.md?page=1)
