# Comptime unique IDs

**URL:** <https://ziggit.dev/t/comptime-unique-ids/5386>\
**Category:** Help\
**Tags:** comptime\
**Created:** [July 27, 2024, 1:25pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386 "2024-07-27T13:25:52Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![biosbob](https://ziggit.dev/user_avatar/ziggit.dev/biosbob/32/1987_2.png) [@biosbob](https://ziggit.dev/u/biosbob)\
**Post date:** [July 27, 2024, 1:25pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/1 "2024-07-27T13:25:52Z")

</div>

i’ve come to appreciate the challenges in maintaining a [global comptime counter](https://ziggit.dev/t/global-comptime-counter/2874) as well as creating [unique IDs for type](https://ziggit.dev/t/generating-unique-id-at-comptime/3944)…

in my use-case, i just need a program-wide unique ID – and one whose “seed” is not necessarily a unique type…

'in fact, i’ve been starting to deploy techniques to ensure that _instances_ of comptime-defined structs are indeed unique (even if initialized identically)… in certain situations, i can maintain a local `comptime var` which i’ll use as a counter… ideally, i’d simply draw upon some comptime unique ID instead… (a μs timestamp would work for me)

what are my options???

---

<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:** [July 27, 2024, 2:27pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/2 "2024-07-27T14:27:47Z")

</div>

This sounds like you are doing something that is not intended to work.

Global counters at comptime if they work somehow, are a bug that you are exploiting. Using such bugs, are counter to the efforts of getting Zig to a state where it can incrementally compile things and compile more things in parallel.

Instead of doing that sort of thing, write code that explicitly registers and collects types in an array and use the index of the type in the array.

I think if you can’t do that, then comptime might not be the right level to implement your features, that is why it isn’t the only way to do meta programming. Instead of exploiting comptime, rethink it, maybe use the buildsystem.

What you describe sounds brittle and like it tries to make things work magically. Instead just write code that actually does things, instead of gluing everything up within the implementation, to then put a pretty interface on top of it.

Throw away the pretty interface and get the pipes simple and clear, write libraries, not frameworks where nobody wants to look at the plumbing.

This is my personal opinion, but this contorting the language into another pseudo language which then is called a framework, seems to bring all kinds of problems.

My 2 cents: either write an actual language implementation or write libraries, not this strange mix. I think you are trying to squeeze your solution into Zig at the wrong level, use buildsteps, or code generation, not a hacked version of comptime.

---

<div class="post-metadata">

**Author:** ![biosbob](https://ziggit.dev/user_avatar/ziggit.dev/biosbob/32/1987_2.png) [@biosbob](https://ziggit.dev/u/biosbob)\
**Post date:** [July 27, 2024, 3:19pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/3 "2024-07-27T15:19:02Z")

</div>

my problem isn’t unique _types_ but rather unique _instances_ of some common struct type that are otherwise identically initialized…

i’ve tried to use the technique suggested by @AndrewCodeDev in [this](https://ziggit.dev/t/const-s-versus-var-s/5358/8) post… it definitely works for me in _some_ but not all situations…

another approach is to have an `.id` field within my struct type, which i would then set to a distinct value per instance… this would make it patently clear that each instance of my struct are truly unique in the eyes of the compiler…

but then, i need a unique identifier at comptime 😉

---

<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:** [July 27, 2024, 3:30pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/4 "2024-07-27T15:30:16Z")

</div>

If those instances are otherwise the same why does it matter whether they are unique or identical?  
Do you mutate them at run-time?  
Do you use them as keys somewhere?

If you create an array of 5 elements you will get 5 elements.  
It is hard to understand why you need this and for what.

It seems like you want to create instances of structs at comptime all scattered around the code base and then use some magic counter to wire them up.

What I am saying is they shouldn’t be scattered all over the place to begin with, that seems like a symptom of trying to make things work through magic, instead of letting the user call some init function that setups some initial state.

---

<div class="post-metadata">

**Author:** ![biosbob](https://ziggit.dev/user_avatar/ziggit.dev/biosbob/32/1987_2.png) [@biosbob](https://ziggit.dev/u/biosbob)\
**Post date:** [July 27, 2024, 3:50pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/5 "2024-07-27T15:50:14Z")

</div>

yes, i mutate them at runtime… that was the issue that @AndrewCodeDev solved in another post…

but for whatever reason, this technique doesn’t seem to do the trick inside an explicit `comptime` block within a `bulkInit` function that is creating many instances of many distinct types…

within the `comptime` block of `bulkInit`, i can indeed maintain a local var which i can increment; and all of the instances created within a _single_ call to this function are indeed unique…

my problem is that i call this `bulkInit` init functions from multiple call-sites – and it’s not at all obvious how to ensure this function’s local comptime var starts off with a _unique_ value on each call…

---

<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:** [July 27, 2024, 4:25pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/6 "2024-07-27T16:25:55Z")

</div>

This feels like playing 20 questions.

Why does it need a unique value?  
What does it produce that uses it?

> [@biosbob](#):
>
> and it’s not at all obvious how to ensure this function’s local comptime var starts off with a _unique_ value on each call…

How about passing the function a unique value?  
(Instead of having a magical comptime generate next id, you could just have some build options that allow a user to customize upper limits like max\_types, max\_callbacks, max\_foo, then you can pass the thing that creates stuff for types 0, the next gets 0+max\_types, the next 0+max\_types+max\_callbacks, and so on, that is one option if you can have some kind of upper limit, but difficult to suggest alternative solutions without knowing what the actual goal is)

What is the purpose? Are you trying to pack things together into memory?  
Are you trying to associate things, etc?

---

<div class="post-metadata">

**Author:** ![biosbob](https://ziggit.dev/user_avatar/ziggit.dev/biosbob/32/1987_2.png) [@biosbob](https://ziggit.dev/u/biosbob)\
**Post date:** [July 27, 2024, 4:40pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/7 "2024-07-27T16:40:42Z")

</div>

here’s a self-contained example:

```zig
const std = @import("std");
const print = std.log.info;

const CA = struct {
    s: *S,
};

const CB = struct {
    s: *S,
    s2: *S2,
};

const S = struct {
    x: u32 = 10,
};

const S2 = struct {
    y: u32 = 5,
};

fn create(T: type, key: anytype) *T {
    return &struct {
        var o = blk: {
            std.mem.doNotOptimizeAway(key);
            break :blk std.mem.zeroInit(T, .{});
        };
    }.o;
}

fn initC(CT: type) CT {
    comptime {
        var new_c: CT = undefined;
        const cti = @typeInfo(CT);
        for (cti.Struct.fields) |fld| {
            const fti = @typeInfo(fld.type);
            const FT = fti.Pointer.child;
            const fval = create(FT, opaque {});
            @field(new_c, fld.name) = fval;
        }
        const res = new_c;
        return res;
    }
}

const ca = initC(CA);
const cb = initC(CB);

pub fn main() void {
    ca.s.x = 20;
    print("ca.s.x = {d}", .{ca.s.x});
    print("cb.s.x = {d}", .{cb.s.x});
    print("cb.2.y = {d}", .{cb.s2.y});
}

```

and the output is:

```zig
info: ca.s.x = 20
info: cb.s.x = 20
info: cb.s2.y = 5

```

clearly `CA` and `CB` are distinct types with different fields… but the `create` function used to instantiate values for fields of type `*S` and `*S2` (suggested by @AndrewCodeDev in [this](https://ziggit.dev/t/const-s-versus-var-s/5358/8) post) doesn’t appear to yield a _distinct_ instance of the `s: *S` field in `CA` and `CB`…

how can ensure that my modification of `cb.s.x` doesn’t _ALSO_ clobber `ca.s.x`???

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [July 27, 2024, 8:41pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/8 "2024-07-27T20:41:17Z")

</div>

This fixes it:

```zig
const std = @import("std");
const print = std.log.info;

const CA = struct {
    s: *S,
};

const CB = struct {
    s: *S,
    s2: *S2,
};

const S = struct {
    x: u32 = 10,
};

const S2 = struct {
    y: u32 = 5,
};

fn create(T: type, name: []const u8, key: anytype) *T {
    return &struct {
        var o = blk: {
            std.mem.doNotOptimizeAway(key);
            std.mem.doNotOptimizeAway(name);
            break :blk std.mem.zeroInit(T, .{});
        };
    }.o;
}

fn initC(CT: type) CT {
    comptime {
        var new_c: CT = undefined;
        const cti = @typeInfo(CT);
        for (cti.Struct.fields, 0..) |fld, i| {
            const fti = @typeInfo(fld.type);
            const FT = fti.Pointer.child;
            const fval = create(FT, @typeName(CT), i);
            @field(new_c, fld.name) = fval;
        }
        const res = new_c;
        return res;
    }
}

const ca = initC(CA);
const cb = initC(CB);

pub fn main() void {
    ca.s.x = 20;
    cb.s.x = 25;
    print("ca.s.x = {d}", .{ca.s.x});
    print("cb.s.x = {d}", .{cb.s.x});
    print("cb.2.y = {d}", .{cb.s2.y});
}

```

Unfortunately, I don’t have a lot of time to automate this process. The thing is, @Sze is right that this process is starting to look brittle to me. At this level, I would start using file generation instead of comptime.

---

<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:** [July 27, 2024, 9:14pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/9 "2024-07-27T21:14:31Z")

</div>

Is that, in fact, combining `doNotOptimizeAway` with comptime execution?

I have trouble modeling what that even does! I thought `doNotOptimizeAway` emitted some ASM, how does that, comptime?

I agree that it would be a mistake to expect whatever is going on there to keep working, I’m impressed that you came up with it in the first place.

---

<div class="post-metadata">

**Author:** ![permutationlock](https://ziggit.dev/user_avatar/ziggit.dev/permutationlock/32/9958_2.png) [@permutationlock](https://ziggit.dev/u/permutationlock)\
**Post date:** [July 27, 2024, 9:25pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/10 "2024-07-27T21:25:16Z")

</div>

> [@mnemnion](#):
>
> I have trouble modeling what that even does! I thought `doNotOptimizeAway` emitted some ASM, how does that, comptime?

I had the same reaction, it turns out it is [an empty return](https://github.com/ziglang/zig/blob/master/lib/std/mem.zig#L4296) at `comptime`. I assume the `anytype` parameter is producing the current behavior, but I have no idea if that is intended to continue.

```zig
pub fn doNotOptimizeAway(val: anytype) void {
    if (@inComptime()) return;

```

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [July 27, 2024, 9:59pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/11 "2024-07-27T21:59:24Z")

</div>

I suspect that for how things are currently working, it just matters if the arguments are being used at all (we’re depending on the `create` function to be monomorphized into unique instances based on the supplied comptime arguments). You and @permutationlock are both correct in the fact that it essentially does nothing. I also do not anticipate that this behaviour will continue through future compiler iterations.

---

<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:** [July 27, 2024, 10:31pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/12 "2024-07-27T22:31:01Z")

</div>

> [@biosbob](#):
>
> 'in fact, i’ve been starting to deploy techniques to ensure that _instances_ of comptime-defined structs are indeed unique (even if initialized identically)… in certain situations, i can maintain a local `comptime var` which i’ll use as a counter… ideally, i’d simply draw upon some comptime unique ID instead… (a μs timestamp would work for me)

Having a “counter var” local to a comptime block is absolutely legitimate, but as others have pointed out any “global” counter would be a violation of the model of comptime as it’s being designed currently. People used to leverage leaks in the abstraction to implement a [userland borrow checker](https://github.com/DutchGhost/zorrow) even :^)

Just like you can’t build a comptime borrow checker anymore, any similar hole will be plugged moving forward.

I’ve toyed with your code sample (the one posted further down in the thread) but wouldn’t find a reasonable way to make it work.

My recommendation is to turn some of that logic into Zig code generated programmatically that then can be imported as a module in your runtime-optimized executable.

If you figure out a not too painful way of getting the code generation part to work, the final integration step should be complete smooth sailing (ie I don’t expect it to have restrictions that catch you by surprise as you try to make it work).

---

<div class="post-metadata">

**Author:** ![biosbob](https://ziggit.dev/user_avatar/ziggit.dev/biosbob/32/1987_2.png) [@biosbob](https://ziggit.dev/u/biosbob)\
**Post date:** [July 27, 2024, 11:08pm UTC](https://ziggit.dev/t/comptime-unique-ids/5386/13 "2024-07-27T23:08:00Z")

</div>

we’re losing sight of my fundamental issue… and i apologize for bringing up “Comptime unique IDs”, as i was already attempting to solve my problem prematurely…

to that end, let’s clean the slate… i just add this [NEW](https://ziggit.dev/t/initializing-constant-structs-containing-pointers-to-mutable-structs/5391) post which hopefully states my underlying issue ore clearly 😉
