# API design and boolean flags

**URL:** <https://ziggit.dev/t/api-design-and-boolean-flags/17885>\
**Category:** Explain\
**Created:** [October 8, 2026, 1:57am UTC](https://ziggit.dev/t/api-design-and-boolean-flags/17885 "2026-10-08T01:57:56Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Illusion](https://ziggit.dev/letter_avatar_proxy/v4/letter/i/e19b73/32.png) [@Illusion](https://ziggit.dev/u/Illusion)\
**Post date:** [October 8, 2026, 1:57am UTC](https://ziggit.dev/t/api-design-and-boolean-flags/17885/1 "2026-10-08T01:57:56Z")

</div>

Let us consider a basic API which has functions which have flags. e.g.

```zig
fn action(..., enable_x: bool, enable_y: bool) {
    ...
    if (enable_x) { ... }
    if (enable_y) { ... }
}

```

and things of that variation.

My mind came up with three forms to approach this.  
One is just the above, which is the clearest in terms of how the API behaves, but, without knowing how the compiler does its thing, makes it unclear how all those flags may be represented. E.g., naively, each of those flags could be its own `u8` on the stack.

By contrast, we have the ol’ C approach where you would use bitmasks. We define a bunch of flags of the form

```zig
const ENABLE_X = 0x0000_0001;
const ENABLE_Y = 0x0000_0010;

```

and then can just define our command as `const command = ENABLE_X | ENABLE_Y` if we wanted both, with the logic in the function doing `& ENABLE_X` or `& ENABLE_Y` to check if it should execute the conditional branches. This makes it clearer how the data is represented, but then the function signature becomes

```zig
fn action(..., flags: u8) { ... }

```

which is less ergonomic in terms of API design. Of course, this could also be a bitset from the standard lib, but the point remains.

The third form I thought was, ok, just use a packed structed with boolean fields. Then, although the function signature is slightly less ergonomic, at least each possible flag is encoded into the type itself. E.g.

```zig
const ActionFlags = packed struct {
    enable_x: bool,
    enable_y: bool,
};
fn action(..., flags: ActionFlags) { ... }

```

Between these three, I view it as deciding between clearest API for a user vs. clearest representation of data. Does anyone have any opinions on which methodology is the best (if there is one), for API design?

---

<div class="post-metadata">

**Author:** ![MeKaLu](https://ziggit.dev/user_avatar/ziggit.dev/mekalu/32/6360_2.png) [@MeKaLu](https://ziggit.dev/u/MeKaLu)\
**Post date:** [October 8, 2026, 7:26am UTC](https://ziggit.dev/t/api-design-and-boolean-flags/17885/2 "2026-10-08T07:26:00Z")

</div>

I prefer the packed struct approach. It is basically a syntax sugar for what you do in C with bitmasks. If you use packed structs you may want to set default values for the fields.

---

<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 8, 2026, 7:36am UTC](https://ziggit.dev/t/api-design-and-boolean-flags/17885/3 "2026-10-08T07:36:40Z")

</div>

I also prefer the (packed) struct approach. A nice thing about it is that you can have presets that are then easy to use for the user for most common things. So something similar to this:

```zig
const Flags = struct {
    a: bool,
    c: enum { variant_1, variant_2 },
    d: u32,

    const low_latency = Flags{ .a = false, .c = .variant_1, .d = 1 };
    const default = Flags{ .a = false, .c = .variant_1, .d = 8 };
    const high_throuput = Flags{ .a = true, .c = .variant_2, .d = 128 };
};

fn action(flags: Flags) !void {
    _ = flags;
    // TODO: do something
}

// Usage
action(.low_latency);
action(.default);
action(.{.a = false, .c = .variant_2, .d = 4096});

```

---

<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 8, 2026, 10:34am UTC](https://ziggit.dev/t/api-design-and-boolean-flags/17885/4 "2026-10-08T10:34:50Z")

</div>

My default would be

```zig
fn action(options: struct {
    x: bool, 
    y: bool, 
}) void

```

The heuristic I use for this sort of decisions is that compiler couldn’t care less about what I write in the source code, that it just produces optimal machine code having an equivalent logical effect, _unless_ there’s some constraint that forces the compiler to externalize the specific representation in the source code.

Two externalizing constraints:

- External ABIs, if I write an `extern fn action`, than whatever ABI I choose for the function, would be its ABI.
- Storing to memory. If I don’t simply pass `Options` as single value, but rather materialize something like `[]Option`, then compiler can’t magically turn my array of pairs of bools into two bitsets.

In the presented case, there are no such externalizing constraints, so I wouldn’t expect to see a function call to `action` in the generated code at all. My default expectation is that everything is inlined in one big wad of code.

This heuristic obviously doesn’t work for simple non-optimizing compilers, and I haven’t _checked_ if it works with `Zig+LLVM` combo, but, given Zig’s whole-program compilation model and ambition to produce optimal software, I’d say that, even if it doesn’t entirely work that way today, that is a toolchain bug.
