From zig-dev
Enforces Zig coding style conventions including naming, formatting, idioms, and best practices. Use when writing new code, reviewing changes, or refactoring for consistency.
How this skill is triggered — by the user, by Claude, or both
Slash command
/zig-dev:zig-style-enforcerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill ensures code follows Zig conventions and idioms for consistency and readability.
This skill ensures code follows Zig conventions and idioms for consistency and readability.
// GOOD
pub fn parseConfigFile(path: []const u8) !Config { }
const max_stack_size = 1024;
var instruction_pointer: usize = 0;
// BAD
pub fn ParseConfigFile(path: []const u8) !Config { } // PascalCase
const MaxStackSize = 1024; // PascalCase
var instructionPointer: usize = 0; // camelCase
// GOOD
pub const Server = struct { };
pub const ConfigFile = struct { };
pub const ValueType = enum { };
// BAD
pub const server = struct { }; // lowercase
pub const config_file = struct { }; // snake_case
pub const value_type = enum { }; // snake_case
// Module-level constants use snake_case
const default_stack_size = 1024;
const max_name_length = 255;
// Enum values use snake_case
pub const Opcode = enum {
label,
func_info,
call,
call_last,
};
// Compile-time parameters use PascalCase
pub fn List(comptime T: type) type {
return struct { };
}
pub const Server = struct {
// Public fields
pub allocator: Allocator,
// Private fields (no pub)
stack: []Value,
registers: []Value,
// Public methods
pub fn init(allocator: Allocator) !Server { }
pub fn deinit(self: *Server) void { }
// Private methods (no pub)
fn executeInstruction(self: *Server) !void { }
};
// 1. Standard library imports
const std = @import("std");
const Allocator = std.mem.Allocator;
// 2. Local imports
const Value = @import("value.zig").Value;
const Config = @import("config.zig").Config;
// 3. Module-level constants
const MAX_STACK_SIZE = 1024;
const DEFAULT_REGISTERS = 256;
// 4. Error sets
pub const ServerError = error{
StackOverflow,
InvalidOpcode,
};
// 5. Type definitions
pub const Server = struct {
// ...
};
// 6. Public functions
pub fn execute(server: *Server) !void {
// ...
}
// 7. Private helper functions
fn validateOpcode(op: u8) bool {
// ...
}
// 8. Tests
test "Server initialization" {
// ...
}
// GOOD: snake_case for files
server.zig
config_parser.zig
value_list.zig
// BAD
Server.zig // PascalCase
configParser.zig // camelCase
const by Default// GOOD: const for immutable
const allocator = std.heap.page_allocator;
const file_path = "config.json";
// BAD: var when const would work
var allocator = std.heap.page_allocator;
// GOOD: var only when needed
var counter: usize = 0;
counter += 1; // Mutated, so var is correct
defer for Cleanup// GOOD: defer immediately after allocation
const buffer = try allocator.alloc(u8, 1024);
defer allocator.free(buffer);
var file = try std.fs.cwd().openFile(path, .{});
defer file.close();
// BAD: defer far from allocation
const buffer = try allocator.alloc(u8, 1024);
// ... lots of code ...
defer allocator.free(buffer); // Easy to miss
errdefer for Error Cleanuppub fn init(allocator: Allocator) !Server {
const stack = try allocator.alloc(Value, 1024);
errdefer allocator.free(stack);
const registers = try allocator.alloc(Value, 256);
errdefer allocator.free(registers);
return Server{
.stack = stack,
.registers = registers,
};
}
// GOOD: Slices include length
pub fn process(data: []const u8) !void {
if (data.len < 4) return error.BufferTooSmall;
// ...
}
// BAD: Raw pointers lose size info
pub fn process(data: [*]const u8, len: usize) !void {
if (len < 4) return error.BufferTooSmall;
// ...
}
pub const Value = union(enum) {
small_int: i32,
big_int: *BigInt,
float: f64,
string: []const u8,
list: *ListValue,
map: *MapValue,
pub fn deinit(self: Value) void {
switch (self) {
.small_int, .float => {}, // No cleanup
.big_int => |bi| bi.deinit(),
.string => |s| s.deinit(),
.list => |l| l.deinit(),
.map => |m| m.deinit(),
}
}
};
pub fn ArrayList(comptime T: type) type {
return struct {
items: []T,
allocator: Allocator,
pub fn init(allocator: Allocator) ArrayList(T) {
return .{
.items = &[_]T{},
.allocator = allocator,
};
}
};
}
// GOOD: Error union provides context
pub fn divide(a: i32, b: i32) !i32 {
if (b == 0) return error.DivisionByZero;
return @divTrunc(a, b);
}
// LESS GOOD: Optional loses error info
pub fn divide(a: i32, b: i32) ?i32 {
if (b == 0) return null; // Why did it fail?
return @divTrunc(a, b);
}
// GOOD: Explicit field names
return Server{
.allocator = allocator,
.stack = stack,
.registers = registers,
.ip = 0,
.sp = 0,
};
// GOOD: Keep lines under 100 characters
const result = try processConfigFile(
allocator,
file_path,
.{ .validate = true, .debug = false },
);
// GOOD: Doc comments for public API
/// Executes the next instruction.
///
/// Returns ServerError if execution fails.
pub fn execute(self: *Server) ServerError!void {
// TODO: Add support for tail call optimization
}
// BAD: Useless comments
// This function executes
pub fn execute(self: *Server) !void { }
// GOOD: Exhaustive switches
switch (value) {
.small_int => |i| std.debug.print("{d}", .{i}),
.float => |f| std.debug.print("{d}", .{f}),
.string => |s| std.debug.print("{s}", .{s}),
.list => |l| printList(l),
.map => |m| printMap(m),
}
// GOOD: Use else for catch-all if needed
switch (opcode) {
.call, .call_last => try self.executeCall(),
.ret => try self.executeReturn(),
else => return error.InvalidOpcode,
}
// GOOD: Propagate errors
pub fn run(self: *Server) !void {
try self.execute();
try self.validate();
}
// GOOD: Handle specific errors
pub fn run(self: *Server) void {
self.execute() catch |err| switch (err) {
error.StackOverflow => self.handleOverflow(),
error.InvalidOpcode => self.handleInvalidOp(),
else => {
std.log.err("Unexpected error: {}", .{err});
return;
},
};
}
// BAD: Silent error swallowing
pub fn run(self: *Server) void {
self.execute() catch {}; // Errors ignored!
}
// BAD
const pBuffer: *u8 = undefined;
const nCount: usize = 0;
// GOOD
const buffer: *u8 = undefined;
const count: usize = 0;
// BAD: Inconsistent naming
pub const Server_State = struct {
currentIP: usize,
stack_pointer: usize,
};
// GOOD: Consistent naming
pub const ServerState = struct {
current_ip: usize,
stack_pointer: usize,
};
// BAD: Comments state the obvious
// Increment the counter
counter += 1;
// GOOD: Comments explain why
// Skip null terminators in name table
if (byte == 0) continue;
// BAD: Global mutable state
var global_server: Server = undefined;
pub fn execute() void {
global_server.run();
}
// GOOD: Pass state explicitly
pub fn execute(server: *Server) void {
server.run();
}
When writing code:
const by default, var only when mutatingdefer immediately after allocationWhen reviewing code:
Use zig fmt to automatically format code:
# Format a single file
zig fmt src/server.zig
# Format entire project
zig fmt src/
Always run zig fmt before committing code.
npx claudepluginhub code0100fun/botfiles --plugin zig-devGuides idiomatic Zig code with comptime, error unions/try/errdefer, structs/enums/optionals, slices/pointers/strings, and resource cleanup via defer.
Guides Zig development with comptime, error handling, slices, optionals, defer/errdefer, and core data types.