Coercion of anonymous struct

Noob here.
I need to call the function pub fn myFn1(ps: PrivStruc) PrivStruct {...} where PrivStruct is a private struct type to which I have no access. The way I do is by passing an anonymous struct as a parameter, something like myFn1(.{ .field1 = 0 }) or myFn1(.{}) where the anonymous struct is coerced into the PrivStruct type. No problem here.

Now, if I create another function:

fn myFn2(anon: anytype) void {
  ...
  const myPS = myFn1(anon);
  ...
}

Then if I call myFn2(.{}) I get an error saying PrivStruct was expected and @TypeOf(.{}) was found, in the myFn1 line. I believe this happens because coercion happens in myFn2 call and after that is pure type compatibility, right ?

Is there a way to myFn1, inside myFn2, to “see” anon as an anonymous struct so that coercion happens ? I have tried different solutions using @TypeOf(anon) but no success.

PrivStruc is private and uses generics and I cannot use it to type any fn or var, at least not in an easy and straight desirable way.

Even tho PrivStruct is private you can still get access to it since its leaked throw myFn1 argument list.

fn myFn2(arg: std.meta.ArgsTuple(myFn1)[0]) void {
  ...
  const myPS = myFn1(arg);
  ...
}
1 Like

Anonymous structs work the same way as regular structs. They are just given an autogenerated name. So casting an anonymous struct to another struct is the same as casting two different structs to each other, which is not allowed.

anytype is generic – monomorphised to specific types at compile time, so the type must be known at the call site.

Your situation is where anyopaque is used.

2 Likes

How anyopaque would be useful? If type is private it would be impossible to construct and pass it to function.

1 Like

The code that does know its type is responsible for allocating/constructing/deallocating/destroying it, and passing opaque pointers to the code that consumes it.

1 Like

I believe that, trying to be succinct, I have oversimplified my problem. Sorry.
OK, I wanna do a function that calls the httpz project
The major httpz files, to understand my need, are http.zig and router.zig

Now my function:

pub fn addRoute(self: *Self, method: httpz.Method, path: []const u8, action: httpz.Action(T), cfg: anytype) !void {
    var router = try self.server.router(.{});
    switch (method) {
        .GET => try router.tryGet(path, action, cfg),
        .POST => try router.tryPost(path, action, cfg),
        .PUT => try router.tryPut(path, action, cfg),
        .DELETE => try router.tryDelete(path, action, cfg),
        .PATCH => try router.tryPatch(path, action, cfg),
        .HEAD => try router.tryHead(path, action, cfg),
        .OPTIONS => try router.tryOptions(path, action, cfg),
        .CONNECT => try router.tryConnect(path, action, cfg),
        .OTHER => unreachable,
    }
}

So, when I pass cfg as .{} to addRoute, I get an error in the corresponding switch branch, when cfg is repassed.

The type of the third parameter (cfg) of the try functions is the router.zig private type RouteConfig(T, httpz.Action(T)). T is the generic type for creating a httpz.Server.

I think you can extract the RouteConfig like this:

cfg: @typeInfo(@TypeOf(@typeInfo(@TypeOf(@FieldType(Self, "server").router)).@"fn".return_type.?.routeConfig)).@"fn".return_type.?

But there is probably a more straightforward way to use the API :slight_smile:

1 Like

No, didn´t work, but thank you tho.

Does using inline switch cases fix it? It’s a feature I only learned of recently.

I can’t find the documentation but this proposal seems to cover it Proposal: Inline Switch Cases · Issue #7224 · ziglang/zig · GitHub

First, I question why you are making a wrapper instead of calling the functions directly.


Shouldn’t, because the switch is in the function after the anonymous struct type is already resolved.


This is just a flaw in the library API, it’s a good rule of thumb to properly expose your generated types and/or type functions so that consumers of your library can do what they want.

If you have access to the router outside your function, you can do thing.addRoute(_, _, _, router.routerConfig(.{...}).

I’d make a const somewhere to store the type for easy access, @Justus2308 was close, the router function returns an error union to a pointer, so there are a couple more steps

This could be simplified a lot, but we don’t have your code to be able to provide a better snippet.

const RouterConfig = @typeInfo(@TypeOf(@typeInfo(@typeInfo(@TypeOf(@typeInfo(@TypeOf(@FieldType(Self, "server").router)).@"fn".return_type.?)).error_union.payload).pointer.child.routeConfig))).@"fn".return_type.?;
4 Likes