cro-practical-zig
SKILL.md
Loris Cro Style Guide
Overview
Loris Cro is the VP of Community at the Zig Software Foundation, known for explaining Zig concepts clearly and demonstrating practical applications. His focus is on making Zig accessible and showing how to build real software.
Core Philosophy
"Zig's build system is one of its killer features."
"Start simple, add complexity only when needed."
Cro emphasizes practical application—building real things, understanding the build system, and using Zig's unique features to solve actual problems.
Design Principles
-
Build System First: Understand
build.zigdeeply. -
Practical Patterns: Focus on what works in production.
-
C Interop: Leverage existing C libraries seamlessly.
-
Incremental Adoption: Use Zig where it helps most.
When Writing Code
Always
- Master the build system early
- Use
build.zigfor all project configuration - Leverage C interop for existing libraries
- Write tests alongside code
- Use
std.logfor structured logging - Profile before optimizing
Never
- Fight the build system—learn it
- Rewrite working C code without reason
- Ignore the standard library—it's excellent
- Skip writing tests
- Optimize without measurements
Prefer
build.zigover external build tools- Standard library over reinvention
- C library bindings over pure Zig rewrites (when sensible)
- Incremental compilation during development
- Cross-compilation from the start
Code Patterns
Build System Mastery
// build.zig - the heart of a Zig project
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Main executable
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
// Link C library
exe.linkLibC();
exe.linkSystemLibrary("sqlite3");
// Add include paths
exe.addIncludePath(.{ .path = "vendor/include" });
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 = .{ .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);
}
C Interoperability
// Import C headers directly
const c = @cImport({
@cInclude("stdio.h");
@cInclude("sqlite3.h");
});
pub fn main() void {
// Call C functions directly
_ = c.printf("Hello from C!\n");
}
// Wrap C libraries idiomatically
const Database = struct {
handle: *c.sqlite3,
pub fn open(path: [*:0]const u8) !Database {
var db: ?*c.sqlite3 = null;
const result = c.sqlite3_open(path, &db);
if (result != c.SQLITE_OK) {
return error.DatabaseOpenFailed;
}
return .{ .handle = db.? };
}
pub fn close(self: *Database) void {
_ = c.sqlite3_close(self.handle);
}
pub fn exec(self: *Database, sql: [*:0]const u8) !void {
var err_msg: ?[*:0]u8 = null;
const result = c.sqlite3_exec(
self.handle,
sql,
null,
null,
&err_msg,
);
if (result != c.SQLITE_OK) {
if (err_msg) |msg| {
std.log.err("SQL error: {s}", .{msg});
c.sqlite3_free(msg);
}
return error.SqlExecutionFailed;
}
}
};
Structured Logging
const std = @import("std");
// Scoped logging
const log = std.log.scoped(.myapp);
pub fn processRequest(request_id: u64) !void {
log.info("Processing request {d}", .{request_id});
const result = doWork() catch |err| {
log.err("Request {d} failed: {}", .{ request_id, err });
return err;
};
log.debug("Request {d} result: {any}", .{ request_id, result });
}
// Configure log level at build time
pub const std_options = struct {
pub const log_level: std.log.Level = .debug;
// Custom log function
pub fn logFn(
comptime level: std.log.Level,
comptime scope: @TypeOf(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
const scope_prefix = if (scope != .default)
"[" ++ @tagName(scope) ++ "] "
else
"";
const prefix = "[" ++ level.asText() ++ "] " ++ scope_prefix;
std.debug.print(prefix ++ format ++ "\n", args);
}
};
Testing Patterns
const std = @import("std");
const testing = std.testing;
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "add basic" {
try testing.expectEqual(@as(i32, 5), add(2, 3));
}
test "add negative" {
try testing.expectEqual(@as(i32, -1), add(2, -3));
}
// Test with allocator
test "dynamic allocation" {
const allocator = testing.allocator; // Detects leaks!
var list = std.ArrayList(u8).init(allocator);
defer list.deinit();
try list.append(42);
try testing.expectEqual(@as(usize, 1), list.items.len);
}
// Fuzz testing
test "fuzz example" {
const input = std.testing.fuzzInput(.{});
// Process fuzz input...
}
Standard Library Gems
const std = @import("std");
// ArrayList - dynamic arrays
fn arrayListExample(allocator: std.mem.Allocator) !void {
var list = std.ArrayList(u32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.appendSlice(&[_]u32{ 3, 4, 5 });
for (list.items) |item| {
std.debug.print("{d} ", .{item});
}
}
// HashMap
fn hashMapExample(allocator: std.mem.Allocator) !void {
var map = std.StringHashMap(u32).init(allocator);
defer map.deinit();
try map.put("one", 1);
try map.put("two", 2);
if (map.get("one")) |value| {
std.debug.print("one = {d}\n", .{value});
}
}
// File I/O
fn fileExample() !void {
const file = try std.fs.cwd().openFile("data.txt", .{});
defer file.close();
var buf_reader = std.io.bufferedReader(file.reader());
var reader = buf_reader.reader();
var line_buf: [1024]u8 = undefined;
while (try reader.readUntilDelimiterOrEof(&line_buf, '\n')) |line| {
std.debug.print("{s}\n", .{line});
}
}
// JSON parsing
fn jsonExample(allocator: std.mem.Allocator) !void {
const json_str =
\\{"name": "Alice", "age": 30}
;
const User = struct {
name: []const u8,
age: u32,
};
const parsed = try std.json.parseFromSlice(
User,
allocator,
json_str,
.{},
);
defer parsed.deinit();
std.debug.print("Name: {s}, Age: {d}\n", .{
parsed.value.name,
parsed.value.age,
});
}
Cross-Compilation
// build.zig - cross-compile easily
pub fn build(b: *std.Build) void {
// Default to native
const target = b.standardTargetOptions(.{});
// Or target specific platforms:
// zig build -Dtarget=x86_64-linux-gnu
// zig build -Dtarget=aarch64-macos
// zig build -Dtarget=x86_64-windows-gnu
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = b.standardOptimizeOption(.{}),
});
b.installArtifact(exe);
}
// Target-specific code
const builtin = @import("builtin");
fn platformSpecific() void {
switch (builtin.os.tag) {
.linux => linuxImpl(),
.macos => macosImpl(),
.windows => windowsImpl(),
else => @compileError("Unsupported platform"),
}
}
Mental Model
Cro approaches Zig projects by asking:
- Is the build system set up right? Start with
build.zig - Can I use an existing C library? Don't reinvent the wheel
- Is this tested? Write tests early and often
- Will this cross-compile? Think portable from the start
- Is this practical? Ship working software
Signature Cro Moves
- Master
build.zigbefore deep language features - C interop for rapid development
- Standard library fluency
- Tests with leak-detecting allocator
- Cross-compilation as default mindset
- Structured logging from the start
Weekly Installs
5
Repository
copyleftdev/sk1llzGitHub Stars
4
First Seen
Feb 1, 2026
Security Audits
Installed on
opencode4
gemini-cli4
antigravity4
claude-code4
github-copilot4
codex4