# How do you write asserts?

**URL:** <https://ziggit.dev/t/how-do-you-write-asserts/17999>\
**Category:** Explain\
**Tags:** language\
**Created:** [October 17, 2026, 6:13am UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999 "2026-10-17T06:13:38Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![kapusta](https://ziggit.dev/letter_avatar_proxy/v4/letter/k/4af34b/32.png) [@kapusta](https://ziggit.dev/u/kapusta)\
**Post date:** [October 17, 2026, 6:13am UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/1 "2026-10-17T06:13:38Z")

</div>

HI,

I started liking asserts to mark every clearly invalid state my function can happen to be run with. But at the same time, I feel like asserts give quite poor debug information - only stacktrace containing line with assertion is printed, which is ok, but forces me to give some descriptions as inline comments, and those can get long.

So, do you also put some inline comments(I do that), or you modify panic handler and use custom assert that also takes comptime message, that is put into some section in the binary where it can be retrived by adress(Now I think I might try doing that)?

Some code for my IRC server/client (WIP):

```zig
fn take_delimiter(line: []const u8, cursor: usize, delimiter: u8) usize {
    std.debug.assert(cursor < line.len); // cursor must point at position in line
    std.debug.assert(line[cursor] == delimiter); // cursor must rest on delimiter

    const newCursor = v: {
        for (cursor.., line[cursor..]) |i, c| {
            if (c != delimiter) break :v i;
        }
        break :v line.len;
    };

    std.debug.assert(cursor < newCursor); // cursor must be moved forward
    std.debug.assert(newCursor <= line.len); // cursor must point at position in line or its end
    if (newCursor == line.len) {
        for (line[cursor..]) |b| std.debug.assert(b == delimiter); // if end, line must consists fully from delimiter
    } else std.debug.assert(line[newCursor] != delimiter); // if not end, cursor must not point at delimiter

    return newCursor;
}

```

(I forgot wdy I didnt use `std.mem.SplitXXX`, but ^ is nicer for me to use)

---

<div class="post-metadata">

**Author:** ![optional](https://ziggit.dev/user_avatar/ziggit.dev/optional/32/10363_2.png) [@optional](https://ziggit.dev/u/optional)\
**Post date:** [October 17, 2026, 9:19am UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/2 "2026-10-17T09:19:31Z")

</div>

I’m new myself so I can’t help you with your actual problem, but the very first thing I’d do is aliasing the command

```zig
const assert = std.debug.assert;

```

---

<div class="post-metadata">

**Author:** ![ali](https://ziggit.dev/user_avatar/ziggit.dev/ali/32/10235_2.png) [@ali](https://ziggit.dev/u/ali)\
**Post date:** [October 17, 2026, 10:19am UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/3 "2026-10-17T10:19:56Z")

</div>

I’d personally put comments _on top_ of the line instead of _in front_.

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [October 17, 2026, 10:32am UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/4 "2026-10-17T10:32:01Z")

</div>

`std.debug.assert` is literally just:

```zig
pub fn assert(ok: bool) void {
    if (!ok) unreachable;
}

```

I encourage everyone to write assert functions (or manual `if`) that better suite their project as often the above is not the best.

---

<div class="post-metadata">

**Author:** ![pzittlau](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/ecae2f/32.png) [@pzittlau](https://ziggit.dev/u/pzittlau)\
**Post date:** [October 17, 2026, 12:27pm UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/5 "2026-10-17T12:27:46Z")

</div>

I only document the complex assertions with a comment. Meaning those that require some additional thinking to get into the right head space for debugging.  
Depending on what mode you expect to build this in I would also remove bounds checks as those are automatically inserted by the Zig compiler.

So assuming a safe release mode I would’ve written your example like this:

```zig
const assert = std.debug.assert;
fn take_delimiter(line: []const u8, cursor: usize, delimiter: u8) usize {
    assert(line[cursor] == delimiter);

    const newCursor = v: {
        for (cursor.., line[cursor..]) |i, c| {
            if (c != delimiter) break :v i;
        }
        break :v line.len;
    };

    assert(cursor < newCursor);
    if (newCursor < line.len) {
        assert(line[newCursor] != delimiter);
    } else {
        assert(newCursor == line.len);
        // if end, line must consists fully from delimiter
        for (line[cursor..]) |b| assert(b == delimiter);
    }

    return newCursor;
}

```

Notice how I’ve also changed the if statement around as it’s more readable for me. And then noticed that the assertion that was directly above(`newCursor <= line.len`) is a bit redundant and could be made simpler by moving it into one of the branches.

Sometimes it’s also helpful to log something before firing the assert. So to build on what @vulpesx said:

```zig
fn take_delimiter(line: []const u8, cursor: usize, delimiter: u8) usize {
    assert(cursor < line.len);
    assert(line[cursor] == delimiter);

    const newCursor = v: {
        for (cursor.., line[cursor..]) |i, c| {
            if (c != delimiter) break :v i;
        }
        break :v line.len;
    };

    assert(cursor < newCursor);
    if (newCursor < line.len) {
        assert(line[newCursor] != delimiter);
    } else {
        assert(newCursor == line.len);
        // if end, line must consists fully from delimiter
        for (cursor..line.len) |i| {
            const b = line[i];
            if (b != delimiter) {
                log.err(
                    "Expected delimiter '{x}'({c}). Got: '{x}'({c}). Index: {x}, Line: {s}",
                    .{ delimiter, delimiter, b, b, i, line },
                );
                unreachable;
            }
        }
    }

    return newCursor;
}

```

Also `std.debug.panic` is a friend.

---

<div class="post-metadata">

**Author:** ![floooh](https://ziggit.dev/user_avatar/ziggit.dev/floooh/32/2989_2.png) [@floooh](https://ziggit.dev/u/floooh)\
**Post date:** [October 17, 2026, 1:52pm UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/6 "2026-10-17T13:52:21Z")

</div>

> I’d personally put comments _on top_ of the line instead of _in front_.

Hmm, do you mean:

```zig
    // val must be null
    assert(val == 0);

```

…but then you don’t see the message in the stack trace when the assert hits:

```zig
/Users/floh/scratch/bla.zig:5:11: 0x1050439af in blub (bla)
    assert(val == 0);

```

…while when you put it on the same line:

```zig
    assert(val == 0); // val must be null

```

…the comment is preserved in the stack trace (which tbh is good enough for me):

```zig
/Users/floh/scratch/bla.zig:4:11: 0x1030ef9af in blub (bla)
    assert(val == 0); // val must be null

```

---

<div class="post-metadata">

**Author:** ![IbrahimOuhamou](https://ziggit.dev/user_avatar/ziggit.dev/ibrahimouhamou/32/2344_2.png) [@IbrahimOuhamou](https://ziggit.dev/u/IbrahimOuhamou)\
**Post date:** [October 17, 2026, 2:36pm UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/7 "2026-10-17T14:36:16Z")

</div>

here is a YouTube short [here](https://youtube.com/shorts/M-VU0fLjIUU?si=15poVAaP7IJDu3QX) from ThePrimegen, I think it’s the first video I saw on the matter and it summarizes the howto  
[another one](https://youtu.be/jc_6ZkkOUpQ?si=ZoHH85_vavzu_9ax)

as for me, when I assume something mentally and it is not apparent in the code, it is then when I do an assert.  
or when I want to ensure a data-structure e.g ‘the root node’s value should always be an empty string’ or ‘a special node should only be placed last in children list’  
or for if statements when I do the `if (!condition) return;`, like  
`if (!logged_in) return ...; if (!allowed_here) return ...; std.debug.assert(cant_think_of_acondition_rn)`

---

<div class="post-metadata">

**Author:** ![matklad](https://ziggit.dev/user_avatar/ziggit.dev/matklad/32/2462_2.png) [@matklad](https://ziggit.dev/u/matklad)\
**Post date:** [October 17, 2026, 5:16pm UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/8 "2026-10-17T17:16:59Z")

</div>

IMO `assert(condition)` is best. Important code has a lot of assertions:

> <https://github.com/tigerbeetle/tigerbeetle/blob/0450c32b2c3c3ab6114f3402cc601341cc8192aa/src/vsr/superblock.zig#L268>

If you require any kind of comments on asserts, you heavily penalize adding assertions.

The great way to ease debugging is to ensure that baseline software has negligible amount of bugs, and to implement thorough generative testing for new functionality:

> **[Unit tests mark territory more than squash bugs](https://yosefk.com/blog/unit-tests-mark-territory-more-than-squash-bugs.html)**

That way, any bug you have is in the new functionality, and is easy to tackle as the code is fresh in your mind.

This is an extreme position, of course, but I think one worth trying!

---

<div class="post-metadata">

**Author:** ![skk64](https://ziggit.dev/letter_avatar_proxy/v4/letter/s/ce7236/32.png) [@skk64](https://ziggit.dev/u/skk64)\
**Post date:** [October 17, 2026, 9:59pm UTC](https://ziggit.dev/t/how-do-you-write-asserts/17999/9 "2026-10-17T21:59:34Z")

</div>

It’s purely a style thing, but in that example I would write the post-asserts like this:

```zig
    assert(cursor < newCursor);
    if (newCursor < line.len) assert(line[newCursor] != delimiter);
    if (newCursor == line.len) assert(std.mem.allEqual(u8, line[cursor..], delimiter));
    if (newCursor > line.len) unreachable;

```

I just think it reads a bit more clearly, but it could just be a personal thing.

But yes, as others say, I try not to rely on comments with asserts (most of the time). If you can’t understand an assert from its context, I would lean more towards rewriting the code/asserts rather than add comments.

Here’s another example from sqlite:

> <https://github.com/sqlite/sqlite/blob/master/src/pager.c#L1123-L1149>

The asserts are easy to understand, without comments, once you read the function documentation. The conditions in the asserts tell you the logic of what is assumed.
