Difference between comptime T: type and T: type in function parameters?

I’m learning Zig and trying to understand the difference between passing a type with comptime vs without it in function parameters.

const std = @import("std");

pub fn main() !void {
    print1(u8, "Hello", "World");
    print2(u8, "Hello", "World");
}

fn print1(comptime T: type, a: []const T, b: []const T) void {
    std.debug.print("{s} {s}\n", .{ a, b });
}

fn print2(T: type, a: []const T, b: []const T) void {
    std.debug.print("{s} {s}\n", .{ a, b });
}

Both functions seem to work fine and give the same output. But I’d like to know:

  • What’s the difference between comptime T: type and just T: type?
  • Is T: type without comptime does it implicitly behave as comptime in this context?
  • When should I use comptime explicitly?

Thank you in advance.

2 Likes

Great question. My understanding is that type is always implicitly comptime. You can see this in action if you try to make a field with type type within a struct.

I’m sure others can come along with a more technical explanation. But basically, types are always handled at comptime in Zig.

3 Likes

It’s actually possible to use type as a struct field (e.g. std.buitlin.Type.StructField) as long as you only use the struct at comptime.

3 Likes

Good clarification! Yea, I meant runtime structs.

2 Likes

There used to be time when type argument had to be marked comptime in functions, but thats not required anymore. Theres basically no difference.

11 Likes