I had an idea to create a generic struct that looks like this:
pub fn Parser(
Context: type,
ParseError: type,
parseValue: fn (ctx: Context, comptime T: type, arg: [:0]const u8) ParseError!T,
) type {
// ...
}
The idea is that the Parser can be passed a custom function to parse args into a set of types, e.g integers, floats, etc. Unfortunately this does not compile due to ‘undeclared identifier T
’. So I wrote a little test program:
const std = @import("std");
pub fn main() !void {
const F = @TypeOf(f);
std.debug.print("{any}\n", .{F});
std.debug.print("{any}\n\n", .{@typeInfo(F).@"fn".return_type});
std.debug.print("{d}\n", .{f(i32)});
std.debug.print("{d}\n", .{f(f32)});
std.debug.print("{s}\n", .{f([]const u8)});
std.debug.print("{any}\n", .{f(bool)});
}
fn f(comptime T: type) T {
return switch (T) {
i32 => 1,
f32 => 2.3,
[]const u8 => "foo bar",
bool => true,
else => comptime unreachable,
};
}
Which outputs:
fn (comptime type) anytype
null
1
2.3
foo bar
true
Interesting. The return type is formatted as anytype
(which you cannot define as a return type of a function), but actually, the return type is null
, i.e there is no return type. There is even a ‘TODO’ in the source for std.builtin.Type.Fn
which is relevant:
/// TODO change the language spec to make this not optional.
return_type: ?type,
This gives me hope that one day my original use case will be supported.
However, I tried anyway to assign f
to a typed function. Trying to use T
or anytype
of course does not work, so I tried type
:
const g: fn (type) type = f;
const G = @TypeOf(g);
std.debug.print("{any}\n", .{G});
std.debug.print("{any}\n\n", .{@typeInfo(G).@"fn".return_type});
std.debug.print("{d}\n", .{g(i32)});
std.debug.print("{d}\n", .{g(f32)});
std.debug.print("{s}\n", .{g([]const u8)});
std.debug.print("{any}\n", .{g(bool)});
Incredibly, this works!
fn (comptime type) anytype # @TypeOf(f)
null
fn (comptime type) type
type
1
2.3
foo bar
true
I then discovered that you can declare g
’s return type as just about anything: (i32
, void
, std.ArrayList(u8)
) and as long as it’s a valid, concrete type expression, Zig just accepts it, regardless of whether g
can or will return that type!
This makes me wonder: is my original use case of declaring a function type’s type parameter as its return type planned? It “works” for now by just declaring the return type as ParseError!void
or something, but this is obviously not ideal for a user-facing API. I could not find any github issues referring to this. I’m posting this to share my discovery and see if anyone has any insight on this, as it feels like a rough edge of the language.