Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Modern Zig project architecture guide. Use when creating Zig projects (systems programming, CLI tools, game dev, high-performance services). Covers explicit allocators, comptime, error handling, and build system.
.claude/skills/majiayu000-zig-project/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 109% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 132% | 0% |
| case-22 | ✓→✗ | ▼ Worse | 315% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 98% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 129% | 0% |
!T for explicit error handling, avoid anyerror> Delete unused code. Change directly. No compatibility layers.
zig// ❌ BAD: Deprecated function kept around /// Deprecated: Use newFunction instead pub fn oldFunction() void { @compileLog("oldFunction is deprecated"); newFunction(); } // ❌ BAD: Alias for renamed functions pub const old_name = new_name; // "for backwards compatibility" // ❌ BAD: Unused parameters fn process(_: *const Config, data: []const u8) !void { _ = data; } // ✅ GOOD: Just delete and update all usages pub fn newFunction() void { // ... } // ✅ GOOD: Remove unused parameters entirely fn process(data: []const u8) !void { // ... }
> Use LiteLLM proxy. Don't call provider APIs directly.
zigconst std = @import("std"); const http = std.http; pub const LLMClient = struct { allocator: std.mem.Allocator, base_url: []const u8, api_key: []const u8, pub fn init(allocator: std.mem.Allocator, base_url: []const u8, api_key: []const u8) LLMClient { return .{ .allocator = allocator, .base_url = base_url, // "http://localhost:4000" .api_key = api_key, }; } pub fn complete(self: *LLMClient, prompt: []const u8, model: []const u8) ![]u8 { // Use OpenAI-compatible API through LiteLLM proxy var client = http.Client{ .allocator = self.allocator }; defer client.deinit(); // Build request to LiteLLM proxy... _ = prompt; _ = model; return ""; } };
bash# Create new project mkdir myapp && cd myapp zig init # Or create executable project zig init-exe # Or create library project zig init-lib
myapp/
├── build.zig # Build configuration (in Zig)
├── build.zig.zon # Package manifest (dependencies)
├── src/
│ ├── main.zig # Entry point (for exe)
│ ├── root.zig # Library root (for lib)
│ └── lib/ # Internal modules
│ └── utils.zig
├── tests/ # Integration tests (optional)
└── lib/ # Vendored dependenciesbuild.zig.zon (Package Manifest)
zig.{ .name = "myapp", .version = "0.1.0", .dependencies = .{ // .some_dep = .{ // .url = "https://github.com/...", // .hash = "...", // }, }, .paths = .{ "build.zig", "build.zig.zon", "src", }, }
build.zig (Build Script)
zigconst std = @import("std"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const exe = b.addExecutable(.{ .name = "myapp", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); b.installArtifact(exe); // Run step const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); const run_step = b.step("run", "Run the application"); run_step.dependOn(&run_cmd.step); // Test step const unit_tests = b.addTest(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); const run_unit_tests = b.addRunArtifact(unit_tests); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_unit_tests.step); }
Every function that allocates must receive an allocator parameter.
zigconst std = @import("std"); // ❌ BAD: Hidden allocation (don't do this) var global_allocator: std.mem.Allocator = undefined; fn badAlloc() ![]u8 { return global_allocator.alloc(u8, 100); } // ✅ GOOD: Explicit allocator fn goodAlloc(allocator: std.mem.Allocator) ![]u8 { return allocator.alloc(u8, 100); }
zigconst std = @import("std"); pub fn main() !void { // General purpose (with safety checks in debug) var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // Arena (bulk alloc/dealloc) var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const arena_alloc = arena.allocator(); // Fixed buffer (no heap) var buffer: [1024]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buffer); const fixed_alloc = fba.allocator(); // Page allocator (direct OS calls) const page_alloc = std.heap.page_allocator; _ = allocator; _ = arena_alloc; _ = fixed_alloc; _ = page_alloc; }
zigfn handleRequest(permanent_allocator: std.mem.Allocator) !void { // Create arena for this request var arena = std.heap.ArenaAllocator.init(permanent_allocator); defer arena.deinit(); // Free ALL request memory at once const allocator = arena.allocator(); // All allocations use arena - no individual frees needed const data = try fetchData(allocator); const processed = try processData(allocator, data); try sendResponse(processed); // arena.deinit() frees everything }
zigconst std = @import("std"); // Define specific error set const FileError = error{ NotFound, AccessDenied, OutOfMemory, EndOfStream, }; // Return error union fn readFile(allocator: std.mem.Allocator, path: []const u8) FileError![]u8 { const file = std.fs.cwd().openFile(path, .{}) catch |err| { return switch (err) { error.FileNotFound => FileError.NotFound, error.AccessDenied => FileError.AccessDenied, else => FileError.NotFound, }; }; defer file.close(); return file.readToEndAlloc(allocator, 1024 * 1024) catch FileError.OutOfMemory; }
zigfn processFile(allocator: std.mem.Allocator, path: []const u8) !void { // try: propagate error up const data = try readFile(allocator, path); errdefer allocator.free(data); // cleanup on error // catch: handle error locally const parsed = parseData(data) catch |err| { std.log.err("Parse failed: {}", .{err}); return err; }; try saveResult(parsed); }
zigfn example() !void { doSomething() catch |err| { std.log.err("Operation failed: {s}", .{@errorName(err)}); return err; }; }
zigfn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } // Usage const result = max(i32, 10, 20); // Returns 20 const float_result = max(f64, 1.5, 2.5); // Returns 2.5
zigpub fn ArrayList(comptime T: type) type { return struct { const Self = @This(); items: []T, capacity: usize, allocator: std.mem.Allocator, pub fn init(allocator: std.mem.Allocator) Self { return .{ .items = &[_]T{}, .capacity = 0, .allocator = allocator, }; } pub fn deinit(self: *Self) void { if (self.capacity > 0) { self.allocator.free(self.items.ptr[0..self.capacity]); } } pub fn append(self: *Self, item: T) !void { // Implementation... _ = item; } }; } // Usage var list = ArrayList(u32).init(allocator); defer list.deinit();
zigfn validateConfig(comptime config: Config) void { if (config.buffer_size == 0) { @compileError("buffer_size must be > 0"); } if (config.buffer_size > 1024 * 1024) { @compileError("buffer_size too large"); } }
zigconst std = @import("std"); const testing = std.testing; fn add(a: i32, b: i32) i32 { return a + b; } test "add positive numbers" { try testing.expectEqual(@as(i32, 5), add(2, 3)); } test "add negative numbers" { try testing.expectEqual(@as(i32, -1), add(1, -2)); }
zigtest "allocation test" { // Use testing allocator for leak detection const allocator = testing.allocator; const data = try allocator.alloc(u8, 100); defer allocator.free(data); try testing.expect(data.len == 100); }
zigtest "expect error" { const result = failingFunction(); try testing.expectError(error.SomeError, result); } test "expect no error" { const result = try successFunction(); try testing.expect(result > 0); }
bash# Run all tests zig build test # Run tests with output zig test src/main.zig # Run specific test zig test src/main.zig --test-filter "add positive"
bash# Build zig build # Debug build zig build -Doptimize=ReleaseFast # Release build # Run zig build run # Build and run # Test zig build test # Run tests # Format zig fmt src/ # Format code # Cross-compile zig build -Dtarget=x86_64-linux-gnu zig build -Dtarget=aarch64-macos zig build -Dtarget=x86_64-windows # Use as C compiler zig cc -o output input.c zig c++ -o output input.cpp
Detailed material starting at ## Checklist has been moved to reference/extended.md to keep this skill concise. Load that reference when the task requires the moved examples, command catalogs, checklists, platform details, or implementation templates.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 15,936 | 15,513 | -3% | 1 | 1 | 0% | 2,647 | 5,521 | +109% | 0 | 0 | — |
case-02 | fail→fail | 21,541 | 14,241 | -34% | 1 | 1 | 0% | 4,302 | 6,086 | +41% | 0 | 0 | — |
case-03 | pass→pass | 15,378 | 11,322 | -26% | 1 | 1 | 0% | 2,624 | 5,185 | +98% | 0 | 0 | — |
case-04 | pass→pass | 12,235 | 7,596 | -38% | 1 | 1 | 0% | 1,959 | 4,484 | +129% | 0 | 0 | — |
case-05 | pass→pass | 15,805 | 11,217 | -29% | 1 | 1 | 0% | 2,643 | 5,084 | +92% | 0 | 0 | — |
case-10 | pass→pass | 11,024 | 7,537 | -32% | 1 | 1 | 0% | 2,149 | 4,557 | +112% | 0 | 0 | — |
case-06 | pass→pass | 11,657 | 11,397 | -2% | 1 | 1 | 0% | 2,246 | 5,253 | +134% | 0 | 0 | — |
case-07 | pass→pass | 20,016 | 13,754 | -31% | 1 | 1 | 0% | 3,721 | 5,746 | +54% | 0 | 0 | — |
case-08 | pass→pass | 9,907 | 8,740 | -12% | 1 | 1 | 0% | 1,713 | 4,706 | +175% | 0 | 0 | — |
case-09 | pass→pass | 7,217 | 6,366 | -12% | 1 | 1 | 0% | 1,276 | 4,192 | +229% | 0 | 0 | — |
case-11 | pass→pass | 9,467 | 3,602 | -62% | 1 | 1 | 0% | 1,603 | 3,627 | +126% | 0 | 0 | — |
case-12 | pass→pass | 6,188 | 5,260 | -15% | 1 | 1 | 0% | 1,097 | 4,014 | +266% | 0 | 0 | — |
case-13 | pass→pass | 7,811 | 2,804 | -64% | 1 | 1 | 0% | 1,177 | 3,491 | +197% | 0 | 0 | — |
case-14 | pass→pass | 7,545 | 3,221 | -57% | 1 | 1 | 0% | 1,205 | 3,529 | +193% | 0 | 0 | — |
case-15 | pass→pass | 8,450 | 5,254 | -38% | 1 | 1 | 0% | 1,515 | 4,050 | +167% | 0 | 0 | — |
case-16 | pass→pass | 4,807 | 3,759 | -22% | 1 | 1 | 0% | 821 | 3,693 | +350% | 0 | 0 | — |
case-17 | pass→pass | 6,621 | 4,288 | -35% | 1 | 1 | 0% | 1,045 | 3,814 | +265% | 0 | 0 | — |
case-18 | pass→pass | 7,346 | 4,067 | -45% | 1 | 1 | 0% | 1,211 | 3,805 | +214% | 0 | 0 | — |
case-19 | fail→pass | 10,429 | 2,813 | -73% | 1 | 1 | 0% | 1,522 | 3,532 | +132% | 0 | 0 | — |
case-20 | pass→pass | 23,910 | 19,845 | -17% | 1 | 1 | 0% | 3,814 | 6,277 | +65% | 0 | 0 | — |
case-21 | pass→pass | 13,927 | 9,545 | -31% | 1 | 1 | 0% | 2,522 | 5,060 | +101% | 0 | 0 | — |
case-22 | pass→fail | 5,645 | 7,410 | +31% | 1 | 1 | 0% | 1,073 | 4,452 | +315% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.