Skip to content
This repository was archived by the owner on Sep 6, 2026. It is now read-only.

Add type checking to simple_router's handle_func - #125

Merged
renerocksai merged 1 commit into
zigzap:masterfrom
cosmicboots:handle_func_introspection
Mar 29, 2025
Merged

renerocksai merged 1 commit into
zigzap:masterfrom
cosmicboots:handle_func_introspection

Conversation

@cosmicboots

Copy link
Copy Markdown
Contributor

I took a quick stab at trying to fix #121.

This is the first time I've played with type introspection in Zig, so please let me know if there's a better way to do this.

Comment thread src/router.zig
const hand_info = @typeInfo(@TypeOf(handler));

// Need to check:
// 1) handler is function pointer

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgive me if what I say don't make sense (my Zig knowledge is minimal at the moment) but wouldn't it be easier if we just change the handler: anytype to be just the signature we need? Like handler: fn(r: zap.Request) void, and there would be no need to do a manual inspection.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That argument sounds compelling at first. But here's where the problem starts. The handler function also must take an instance (to a context) orelse all you could access from within the handler function would be global variables.

IMHO the comment above handle_func explains it:

/// Call this to add a route with a handler that is bound to an instance of a struct.
/// Example:
///
/// ```zig
/// const HandlerType = struct {
///     pub fn getA(self: *HandlerType, r: zap.Request) void {
///         _ = self;
///         r.sendBody("hello\n\n") catch return;
///     }
/// }
/// var handler_instance = HandlerType{};
///
/// my_router.handle_func("/getA", &handler_instance, HandlerType.getA);

See, getA takes a self instance of type *HandlerType and only then the Request.

So there is no single static fn type for handler.

Related: In languages like Python, you can take a reference to an instance method—effectively a “function pointer.” For example, if your class defines a method like def get(self, request), you can instantiate the class with instantiated_object = MyClass() and then assign the method to a variable with handler = instantiated_object.get. This handler is bound to the instance, meaning you can call it with just the request (handler(just_a_request)) rather than needing to pass the instance explicitly (handler(instantiated_object, request)).

Zig doesn’t work like that, though. I recently wrote in detail about this difference [here](https://renerocks.ai/blog/zig-bound-functions/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, for handlers that don't need an instance, there is:

/// Call this to add a route with an unbound handler: a handler that is not member of a struct.
pub fn handle_func_unbound(self: *Router, path: []const u8, h: zap.HttpRequestFn) !void {

Here, since no "dynamic typing" is needed, the type of the handler function h is fixed.

@renerocksai

Copy link
Copy Markdown
Member

Thx, it's fine! And sorry it took so long!

@renerocksai
renerocksai merged commit 4d8ba50 into zigzap:master Mar 29, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TODO: make Router more strict wrt types of handler functions

3 participants