Self referential comptime struct parameters

I have a function which takes in comptime type parameters and returns a struct. I’ll put in a simplified example:

fn Wrap(comptime to_wrap: type, comptime: tail: type) type {
  return struct {
    inner: to_wrap,
    tail: tail,
  };
}

I’ve run into a roadblock where I need to create a Wrap where the type of tail is ?*Wrap. I’d love to be able to do something like this:

const Node = Wrap(InnerType, ?*Node);

But this creates a circular reference which the compiler can not handle.

For a bit of justification, This Wrap function in my code is creating a struct which allows you to have a header and variable length buffer right next to each other in memory much like: https://github.com/ziglang/zig/issues/173

Does anyone have advice on ways to handle this?

I may be misunderstanding your question (we may need an example to see exactly what you’re going for), but it sounds like you’re looking for a *@This() strategy. Essentially, you’re composing a linked list on the fly… am I right?

@finnamon Welcome to ziggit :slight_smile:

error: dependency loop detected
const Node = Wrap(usize, ?*Node);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

a workaround for this bug is to use:

const Node = Wrap(InnerType, ?*opaque {});

*opaque{} is a generic pointer, you can put anything in, but to get out a *Node you need to cast:

const node: ?*Node = @ptrCast(@alignCast(tail))

see also: Avoid "dependency loop detected" with function pointers - #4 by dimdin

1 Like

Yes, some way of using @This() when supplying inputs to Wrap would be a solution. The example I gave may have been a little too simple to express what I’m trying to do, I can also supply the actual code for Wrap if that would be helpful.

Thanks for the workaround! I probably should have mentioned in my original post that I was trying to avoid this solution, but it does get the job done.

I would really like to see some way of keeping type safety here, but I understand if that’s just not possible.

1 Like

There are plenty of workarounds that are type safe, but they probably aren’t what you’re looking for:

const UsePtrToThis = struct { };

fn Wrap(comptime to_wrap: type, comptime tail: type) type {
  return struct {
    inner: to_wrap,
    tail: if (tail == UsePtrToThis) ?*@This() else tail
  };
}
1 Like

There are ways to make this type safe, the question is, how much do you need Wrap to do? Will the tail always be an optional example of the type? Is it ok to have two functions, one for when the tail isn’t self-referential, and one for when it is?

1 Like

This solution is certainly good enough for what I need to accomplish which is great! I think that the only way for comp time defined types to have self referential arguments while not requiring any hacks like this would be to evaluate comp time expressions lazily which would be a pretty big change I’m guessing.