# Assert but only in paranoid mode

**URL:** <https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934>\
**Category:** Explain\
**Created:** [May 2, 2025, 12:59pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934 "2025-05-02T12:59:26Z")\
**Posts on this page:** 20\
**Page:** 1

<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 2, 2025, 12:59pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/1 "2025-05-02T12:59:26Z")

</div>

I am testing some routines which I need to assert, but I only want this if the program (debug compile) is in ‘paranoid’ mode.  
These paranoid assertions should only be executed when my global const paranoid is true.

How can I best write a routine `paranoid_assert()` of which I am sure it is totally ignored in release mode?  
Normal asserts are ignored in releasemode.

(this is kinda needed because the paranoid checks in some cases are slowing down very much).

---

<div class="post-metadata">

**Author:** ![Calder-Ty](https://ziggit.dev/user_avatar/ziggit.dev/calder-ty/32/10237_2.png) [@Calder-Ty](https://ziggit.dev/u/Calder-Ty)\
**Post date:** [May 2, 2025, 1:23pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/2 "2025-05-02T13:23:06Z")

</div>

EDIT: I don’t think this actually works

> **Summary**
>
> Something like:
> 
> ```zig
> fn paranoid_assert(ok: bool) void {
> if (paranoid and !ok) unreachable
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [May 2, 2025, 1:23pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/3 "2025-05-02T13:23:56Z")

</div>

the simplest way is to import `builtin` and to check if your program is being built in debug mode, and only perform the check in that case. for more granular control you can add a build-time setting (see [Zig Build System ⚡ Zig Programming Language](https://ziglang.org/learn/build-system/)) but the gist is the same.

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

fn debugAssert(ok: bool) void {
  if (builtin.mode != .Debug) return;
  if (!ok) unreachable;
}
```

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [May 2, 2025, 1:24pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/4 "2025-05-02T13:24:11Z")

</div>

In build.zig:

```zig
const paranoid = b.option(bool, "paranoid", "Whether to enable paranoid assertions") orelse false;
const options = b.addOptions();
options.addOption(bool, "paranoid", paranoid);
const exe = b.addExecutable(
  // Add the usual stuff
  .imports = &.{ .{ .name = "paranoid", .module = option.createModule() },
);

```

In your program:

```zig
const paranoid = @import("paranoid").paranoid;

// Whenever you need to assert something in paranoid mode:
if(comptime paranoid) std.debug.assert(assertion);

```

You can’t simple pass the assertion into the function, because the work to compute the parameter would have already been done, so you need to wrap it in a conditional at the call site.

---

<div class="post-metadata">

**Author:** ![Calder-Ty](https://ziggit.dev/user_avatar/ziggit.dev/calder-ty/32/10237_2.png) [@Calder-Ty](https://ziggit.dev/u/Calder-Ty)\
**Post date:** [May 2, 2025, 1:26pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/5 "2025-05-02T13:26:19Z")

</div>

> You can’t simple pass the assertion into the function, because the work to compute the parameter would have already been done, so you need to wrap it in a conditional at the call site.

Hmm that’s a good point i didn’t think about. I was kind thinking that if the condition was dependent on a comptime known value it would be removed.

---

<div class="post-metadata">

**Author:** ![kristoff](https://ziggit.dev/user_avatar/ziggit.dev/kristoff/32/9_2.png) [@kristoff](https://ziggit.dev/u/kristoff)\
**Post date:** [May 2, 2025, 1:28pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/6 "2025-05-02T13:28:17Z")

</div>

Yes good point, sometimes you will have to “hoist” the setting check.

---

<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, 1:36pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/7 "2025-05-02T13:36:50Z")

</div>

But with inline you also could get the if to be inserted at the call site.

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [May 2, 2025, 2:20pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/8 "2025-05-02T14:20:16Z")

</div>

The computation needs to be inside the escope of the if clause.

Consider this:

```zig
inline fn paranoidAssert(ok: bool) void{
  if(comptime paranoid) std.debug.assert(ok);
}

// Call site
paranoidAssert(expensiveComputation());

```

After inlining, it would become:

```zig
const ok = expensiveComputation();
if(comptime paranoid) std.debug.assert(ok);

```

Even though `ok` is a dead value, in debug builds dead values are not eliminated, so you’d pay the cost the same way.

---

<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 2, 2025, 2:36pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/9 "2025-05-02T14:36:37Z")

</div>

Maybe it is easiest to do this?

```zig
fn somewhere_in_my_program() void {
    assert(my_expensive_check());
}

fn my_expensive_check() bool {
   if (!globals.paranoid) return true;
   // do my checks
}

```

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [May 2, 2025, 3:45pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/10 "2025-05-02T15:45:42Z")

</div>

Yes, that would work as well, but only for `expensiveCheck`. Regardless of whichever method you choose, for every check that you want to make conditional, you need a way to divert control flow before reaching the computation. You can put it in a function or do an inline block of code. What you can’t do is have a function that takes an arbitrary statement as a parameter and conditionally executes it. This goes agaisn’t the principal of “no hidden control flow”. Consider what would happen if such a function existed:

```zig
paranoidAssert(functionWithSideEffects());

```

Without knowing what `paranoidAssert` is doing internally, one would expect the side effects to happen. It would be very surprising and hidden if it could make the entire call to `functionWithSideEffects` disappear.

Taking a page out of asynchronous programming, what you could do is pass a function object:

```zig
/// `assertion` must have a method `call`
/// that returns a bool, and will be called
/// if paranoid assertions are enabled.
fn paranoidAssert(assertion: anytype) void{
  if(comptime paranoid) 
    std.debug.assert(assertion.call());
}

// Call site
paranoidAssertion(struct{
  // arguments
  arg1: u8,

  pub fn call(self: @This()) bool{
    //Do the checks
  }
}{ .arg1 = 1});

```

But it’s a bit clumsy. Better to just do `if(comptime paranoid)` thing.

---

<div class="post-metadata">

**Author:** ![castholm](https://ziggit.dev/user_avatar/ziggit.dev/castholm/32/1330_2.png) [@castholm](https://ziggit.dev/u/castholm)\
**Post date:** [May 2, 2025, 4:03pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/11 "2025-05-02T16:03:27Z")

</div>

```zig
if (paranoid) assert(expensiveCheck())

```

This fine and probably the most concise way of eliding the expensive checks when assertions are disabled. However, you could also consider something like the following, where the assertion function is an optional that you have to unwrap:

```zig
const paranoid = false; // from build options

const assertParanoid: ?fn (ok: bool) void = if (paranoid)
    struct {
        fn assert(ok: bool) void {
            std.debug.assert(ok);
        }
    }.assert
else
    null;

pub fn main() void {
    if (assertParanoid) |assert| assert(expensiveCheck());
}

fn expensiveCheck() bool {
    std.debug.print("checked!\n", .{});
    return true;
}

```

This is slightly more verbose, but leaves less room for programmer mistakes since the captured `assert` is guaranteed to only be available in that scope.

---

<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:23pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/12 "2025-05-02T16:23:21Z")

</div>

> [@castholm](#):
>
> `if (paranoid) assert(expensiveCheck())`

If you want to make certain that this is a comptime operation (which it should be anyway) you can just say

```zig
if (comptime paranoid)
    assert(expensiveCheck());

```

That will get you a compile error if `paranoid` isn’t comptime-known for some reason.

---

<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 2, 2025, 4:34pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/13 "2025-05-02T16:34:41Z")

</div>

ah yes. that is clearer and better readable. no hidden things.

---

<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 31, 2025, 12:32pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/14 "2025-05-31T12:32:53Z")

</div>

Coming back here. Is there a convenient oneliner to assert this?

```zig
fn tech_check() SomeError!void {
    if (this_or_that) return SomeError.Omg;
}

```

```zig
std.debug.assert(tech_check() == void); // does not compile;
std.debug.assert(tech_check() == {}); // does not compile;

```

---

<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:** [May 31, 2025, 12:37pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/15 "2025-05-31T12:37:19Z")

</div>

```zig
std.debug.assert(if(tech_check())|_| true else false);
std.debug.assert(std.meta.isErr(tech_check()));

```

`isErr` is just a wrapper for the if

---

<div class="post-metadata">

**Author:** ![castholm](https://ziggit.dev/user_avatar/ziggit.dev/castholm/32/1330_2.png) [@castholm](https://ziggit.dev/u/castholm)\
**Post date:** [May 31, 2025, 12:49pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/16 "2025-05-31T12:49:43Z")

</div>

> [@ericlang](#):
>
> Is there a convenient oneliner to assert this?

`tech_check() catch unreachable`

> [@vulpesx](#):
>
> ```zig
> std.debug.assert(if(tech_check())|_| true else false);
> std.debug.assert(std.meta.isErr(tech_check()));
> 
> ```

Just so no one gets confused it’s `std.debug.assert(if (tech_check()) |_| true else |_| false)`. Zig is really picky about the `|_|` payload captures.

---

<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:** [May 31, 2025, 12:50pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/17 "2025-05-31T12:50:26Z")

</div>

oops, yeah that counts as ignoring the error

---

<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 31, 2025, 12:53pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/18 "2025-05-31T12:53:18Z")

</div>

> [@vulpesx](#):
>
> `std.debug.assert(std.meta.isErr(tech_check()));`

yeah we need a `!` 🙂

> [@castholm](#):
>
> tech\_check() catch unreachable

omg really… this does not fire in releasemode?

---

<div class="post-metadata">

**Author:** ![castholm](https://ziggit.dev/user_avatar/ziggit.dev/castholm/32/1330_2.png) [@castholm](https://ziggit.dev/u/castholm)\
**Post date:** [May 31, 2025, 12:56pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/19 "2025-05-31T12:56:36Z")

</div>

`std.debug.assert` is defined like this

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

```

so it is functionally identical to `catch unreachable`, which will be UB in non-safety-checked release modes and can result in literally anything happening because the compiler/optimizer is free to pretend that that code path doesn’t exist. You should neither use `std.debug.assert` nor `catch unreachable` if you expect that your assertions will ever fail. If there’s even the slightest chance that an assertion might not pass you should use `@panic` or something similar instead.

---

<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:** [May 31, 2025, 12:57pm UTC](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934/20 "2025-05-31T12:57:27Z")

</div>

lol i thought of `catch unreachable` but went “that won’t trip the assert” meanwhile assert: `if(!ok) unreachable`.

in release safe it still trips, as does `std.debug.assert`

[Next page](https://ziggit.dev/t/assert-but-only-in-paranoid-mode/9934.md?page=2)
