zig-comptime

Guide agents through Zig's comptime system: compile-time function evaluation, comptime type parameters, generics via anytype , type reflection with @typeInfo , and metaprogramming patterns that replace C++ templates and macros.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "zig-comptime" with this command: npx skills add mohitmishra786/low-level-dev-skills/mohitmishra786-low-level-dev-skills-zig-comptime

Zig comptime

Purpose

Guide agents through Zig's comptime system: compile-time function evaluation, comptime type parameters, generics via anytype , type reflection with @typeInfo , and metaprogramming patterns that replace C++ templates and macros.

Triggers

  • "How does comptime work in Zig?"

  • "How do I write a generic function in Zig?"

  • "How do I use @typeInfo for reflection?"

  • "How do I generate code at compile time in Zig?"

  • "How does anytype work in Zig?"

  • "How do Zig generics compare to C++ templates?"

Workflow

  1. comptime basics

// comptime keyword forces compile-time evaluation const x: comptime_int = 42; // comptime integer (arbitrary precision) const y: comptime_float = 3.14159; // comptime float (arbitrary precision)

// comptime block — runs at compile time comptime { const val = fibonacci(20); // computed at compile time std.debug.assert(val == 6765); // compile-time assertion }

// comptime parameter — caller must provide a comptime-known value fn makeArray(comptime T: type, comptime n: usize) [n]T { return [_]T{0} ** n; // array of n zeros of type T }

const arr = makeArray(f32, 8); // [8]f32 computed at compile time

  1. Generic functions with comptime type parameters

const std = @import("std");

// Generic max function — T must be comptime-known fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; }

// Usage: T is inferred from arguments or specified explicitly const r1 = max(i32, 3, 7); // 7 const r2 = max(f64, 2.5, 1.8); // 2.5

// Generic Stack data structure fn Stack(comptime T: type) type { return struct { items: []T, top: usize, allocator: std.mem.Allocator,

    const Self = @This();

    pub fn init(allocator: std.mem.Allocator) !Self {
        return Self{
            .items = try allocator.alloc(T, 64),
            .top = 0,
            .allocator = allocator,
        };
    }

    pub fn push(self: *Self, value: T) void {
        self.items[self.top] = value;
        self.top += 1;
    }

    pub fn pop(self: *Self) ?T {
        if (self.top == 0) return null;
        self.top -= 1;
        return self.items[self.top];
    }

    pub fn deinit(self: *Self) void {
        self.allocator.free(self.items);
    }
};

}

// Usage: Stack(i32) and Stack(f64) are distinct types var int_stack = try Stack(i32).init(allocator); defer int_stack.deinit(); int_stack.push(42);

  1. anytype — duck-typed comptime parameters

anytype accepts any type and the compiler infers it at the call site:

// anytype: function works for any type with .len field fn printLength(thing: anytype) void { std.debug.print("Length: {}\n", .{thing.len}); }

printLength("hello"); // string literal — works printLength([_]u8{1, 2, 3}); // array — works printLength(std.ArrayList(u32){}); // ArrayList — works

// anytype with comptime checks for better errors fn serialize(writer: anytype, value: anytype) !void { // Verify writer has write method at comptime if (!@hasDecl(@TypeOf(writer), "write")) { @compileError("writer must have a write method"); } try writer.write(std.mem.asBytes(&value)); }

// anytype in struct methods (used throughout std library) pub fn format( self: MyType, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, // any writer: file, buffer, etc. ) !void { try writer.print("{} {}", .{self.x, self.y}); }

  1. Type reflection with @typeInfo

@typeInfo returns a tagged union describing a type's structure at comptime:

const std = @import("std"); const TypeInfo = std.builtin.Type;

fn printTypeInfo(comptime T: type) void { const info = @typeInfo(T); switch (info) { .Int => |i| std.debug.print("Int: {} bits, {s}\n", .{i.bits, @tagName(i.signedness)}), .Float => |f| std.debug.print("Float: {} bits\n", .{f.bits}), .Struct => |s| { std.debug.print("Struct with {} fields:\n", .{s.fields.len}); inline for (s.fields) |field| { std.debug.print(" {s}: {}\n", .{field.name, field.type}); } }, .Enum => |e| { std.debug.print("Enum with {} values:\n", .{e.fields.len}); inline for (e.fields) |field| { std.debug.print(" {s} = {}\n", .{field.name, field.value}); } }, .Optional => |o| std.debug.print("Optional({s})\n", .{@typeName(o.child)}), .Array => |a| std.debug.print("[{}]{s}\n", .{a.len, @typeName(a.child)}), else => std.debug.print("Other type: {s}\n", .{@typeName(T)}), } }

// Usage at comptime comptime { printTypeInfo(u32); } // Int: 32 bits, unsigned comptime { printTypeInfo(f64); } // Float: 64 bits

  1. Comptime-generated code patterns

// Generate a lookup table at comptime const sin_table = blk: { const N = 256; var table: [N]f32 = undefined; @setEvalBranchQuota(10000); // increase for expensive comptime eval for (0..N) |i| { const angle = @as(f32, @floatFromInt(i)) * (2.0 * std.math.pi / N); table[i] = @sin(angle); } break :blk table; };

// Comptime string processing fn upperCase(comptime s: []const u8) [s.len]u8 { var result: [s.len]u8 = undefined; for (s, 0..) |c, i| { result[i] = std.ascii.toUpper(c); } return result; } const HELLO = upperCase("hello"); // computed at compile time

// Structural typing: accept any struct with specific fields fn area(shape: anytype) f64 { const T = @TypeOf(shape); if (@hasField(T, "width") and @hasField(T, "height")) { return @as(f64, shape.width) * @as(f64, shape.height); } else if (@hasField(T, "radius")) { return std.math.pi * @as(f64, shape.radius) * @as(f64, shape.radius); } else { @compileError("shape must have width+height or radius fields"); } }

  1. comptime vs C++ templates comparison

Feature C++ templates Zig comptime

Syntax template<typename T>

fn foo(comptime T: type)

Error messages Cryptic instantiation stacks Clear, at definition point

Specialization template<> class Foo<int>

if (T == i32) { ... } with inline if

SFINAE Complex enable_if @hasDecl , @hasField , @compileError

Variadic template<typename... Ts>

anytype , tuples, inline for

Compile time Can be very slow Explicit, bounded by @setEvalBranchQuota

Values Requires constexpr

Any expression can be comptime

Macros Separate #define system Comptime functions replace most macros

  1. Common comptime patterns

// Pattern: compile error for unsupported types fn serializeInt(comptime T: type, value: T) []const u8 { if (@typeInfo(T) != .Int) { @compileError("serializeInt requires an integer type, got: " ++ @typeName(T)); } // ... }

// Pattern: conditional compilation const is_debug = @import("builtin").mode == .Debug; if (comptime is_debug) { // included only in debug builds validateInvariant(self); }

// Pattern: inline for over comptime-known slice const fields = std.meta.fields(MyStruct); inline for (fields) |field| { // field.name, field.type available at comptime std.debug.print("{s}\n", .{field.name}); }

Related skills

  • Use skills/zig/zig-testing for comptime assertions and testing comptime code

  • Use skills/zig/zig-build-system for comptime-based build.zig configuration

  • Use skills/compilers/cpp-templates for C++ template equivalent patterns

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cmake

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

static-analysis

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

llvm

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gdb

No summary provided by upstream source.

Repository SourceNeeds Review