748 lines
15 KiB
Markdown
748 lines
15 KiB
Markdown
# Zig Language Basics Reference (Zig 0.16.0)
|
|
|
|
Core language features, control flow, and type system fundamentals.
|
|
|
|
Primary Zig 0.16 release-note source: https://ziglang.org/download/0.16.0/release-notes.html
|
|
|
|
Key 0.16 language changes:
|
|
|
|
- `@Type` removed; use specific type-construction builtins.
|
|
- `@cImport` is deprecated as the long-term C translation API; prefer build-system translation.
|
|
- Runtime vector indexes are forbidden.
|
|
- Vectors and arrays no longer support in-memory coercion.
|
|
- Returning the address of an expired local is diagnosed.
|
|
- Packed structs/unions cannot contain pointers.
|
|
- Packed unions and extern packed/enum layouts require more explicit backing types.
|
|
- Type resolution is lazier in some cases and stricter in others; investigate new dependency-loop diagnostics.
|
|
|
|
## Table of Contents
|
|
- [Types](#types)
|
|
- [Control Flow](#control-flow)
|
|
- [Error Handling](#error-handling)
|
|
- [Optionals](#optionals)
|
|
- [Structs](#structs)
|
|
- [Enums](#enums)
|
|
- [Unions](#unions)
|
|
- [Pointers and Slices](#pointers-and-slices)
|
|
- [Comptime](#comptime)
|
|
- [Functions](#functions)
|
|
|
|
## Types
|
|
|
|
### Primitive Types
|
|
```zig
|
|
// Integers (signed and unsigned, including arbitrary-width integer types)
|
|
i8, i16, i32, i64, i128, isize // signed
|
|
u8, u16, u32, u64, u128, usize // unsigned
|
|
i7, u24, i53 // arbitrary widths
|
|
|
|
// Floats
|
|
f16, f32, f64, f80, f128
|
|
|
|
// Other
|
|
bool // true or false
|
|
void // zero-size type
|
|
noreturn // function never returns
|
|
type // type of types (comptime only)
|
|
anyopaque // type-erased pointer target
|
|
comptime_int // arbitrary precision integer (comptime only)
|
|
comptime_float // arbitrary precision float (comptime only)
|
|
|
|
// C interop types
|
|
c_char, c_short, c_int, c_long, c_longlong
|
|
c_ushort, c_uint, c_ulong, c_ulonglong
|
|
c_longdouble
|
|
```
|
|
|
|
### Type Coercion
|
|
```zig
|
|
// Implicit coercions (safe, automatic)
|
|
const a: u16 = 42; // comptime_int → u16
|
|
const b: i32 = a; // u16 → i32 (widening)
|
|
const c: f64 = 3.14; // comptime_float → f64
|
|
const d: []const u8 = "hello"; // *const [5:0]u8 → []const u8
|
|
const e: ?i32 = 5; // i32 → ?i32
|
|
|
|
// @as for explicit safe coercion
|
|
const x = @as(u32, 100);
|
|
|
|
// Casts for unsafe/reinterpret conversions
|
|
const y: u8 = @intCast(big_value); // may panic if value doesn't fit
|
|
const z: u32 = @bitCast(float_val); // reinterpret bits
|
|
```
|
|
|
|
### Arrays
|
|
```zig
|
|
// Fixed-size arrays
|
|
const arr: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
|
var mutable_arr: [5]u8 = .{ 1, 2, 3, 4, 5 };
|
|
const arr2 = [_]u8{ 1, 2, 3 }; // infer length
|
|
const zeros = [_]u8{0} ** 100; // repeat pattern
|
|
|
|
// Sentinel-terminated arrays
|
|
const str: [5:0]u8 = "hello".*; // null-terminated
|
|
const arr: [3:255]u8 = .{ 1, 2, 3 }; // 255-terminated
|
|
|
|
// Access
|
|
const elem = arr[2];
|
|
const len = arr.len;
|
|
|
|
// Iteration
|
|
for (arr) |elem| { ... }
|
|
for (arr, 0..) |elem, i| { ... } // with index
|
|
for (&mutable_arr) |*elem| { elem.* = 0; } // mutable
|
|
```
|
|
|
|
### Tuples
|
|
```zig
|
|
const tuple = .{ 42, "hello", true };
|
|
const first = tuple[0]; // 42
|
|
const len = tuple.len; // 3
|
|
|
|
// Destructuring
|
|
const a, const b, const c = tuple;
|
|
```
|
|
|
|
## Control Flow
|
|
|
|
### if
|
|
```zig
|
|
// Basic
|
|
if (condition) {
|
|
// ...
|
|
} else if (other) {
|
|
// ...
|
|
} else {
|
|
// ...
|
|
}
|
|
|
|
// Expression form
|
|
const value = if (condition) x else y;
|
|
|
|
// With optionals
|
|
if (optional_value) |unwrapped| {
|
|
// unwrapped is non-null
|
|
} else {
|
|
// was null
|
|
}
|
|
|
|
// With error unions
|
|
if (error_union) |value| {
|
|
// success
|
|
} else |err| {
|
|
// handle err
|
|
}
|
|
```
|
|
|
|
### switch
|
|
```zig
|
|
const result = switch (value) {
|
|
1 => "one",
|
|
2, 3 => "two or three",
|
|
4...10 => "four to ten",
|
|
else => "other",
|
|
};
|
|
|
|
// Capture
|
|
switch (tagged_union) {
|
|
.variant => |payload| { ... },
|
|
.other => |*ptr| { ptr.* = new_value; }, // mutable capture
|
|
}
|
|
|
|
// Comptime switch on types
|
|
switch (@typeInfo(T)) {
|
|
.int => |info| { ... },
|
|
.float => { ... },
|
|
else => @compileError("unsupported type"),
|
|
}
|
|
```
|
|
|
|
### Labeled switch (0.14.0+) - State Machines
|
|
```zig
|
|
state: switch (initial_state) {
|
|
.idle => {
|
|
continue :state .running; // transition
|
|
},
|
|
.running => {
|
|
if (done) break :state result; // exit with value
|
|
continue :state .running; // loop
|
|
},
|
|
.error => return error.Failed,
|
|
}
|
|
```
|
|
|
|
### Non-exhaustive enum switch (0.15.x)
|
|
```zig
|
|
switch (non_exhaustive_enum) {
|
|
.known_a => {},
|
|
.known_b => {},
|
|
else => {}, // other named tags
|
|
_ => {}, // unnamed integer values
|
|
}
|
|
```
|
|
|
|
### while
|
|
```zig
|
|
// Basic
|
|
while (condition) { ... }
|
|
|
|
// With else (runs if condition was never true or on break)
|
|
while (condition) { ... } else { ... }
|
|
|
|
// With continue expression
|
|
var i: usize = 0;
|
|
while (i < 10) : (i += 1) { ... }
|
|
|
|
// With optional
|
|
while (iterator.next()) |item| { ... }
|
|
|
|
// With error union
|
|
while (reader.readByte()) |byte| {
|
|
...
|
|
} else |err| {
|
|
if (err != error.EndOfStream) return err;
|
|
}
|
|
|
|
// Infinite loop
|
|
while (true) { ... }
|
|
```
|
|
|
|
### for
|
|
```zig
|
|
// Iterate slice/array
|
|
for (items) |item| { ... }
|
|
|
|
// With index
|
|
for (items, 0..) |item, i| { ... }
|
|
|
|
// Multiple sequences (must have same length)
|
|
for (a, b, c) |x, y, z| { ... }
|
|
|
|
// Mutable iteration
|
|
for (&items) |*item| { item.* = new_value; }
|
|
|
|
// Runtime bounds work with an ordinary for loop.
|
|
for (start..end) |i| { ... }
|
|
|
|
// Use inline for when the range is comptime-known and should be unrolled.
|
|
inline for (0..10) |i| { ... }
|
|
```
|
|
|
|
### Labels and Control
|
|
```zig
|
|
// Labeled blocks
|
|
const result = blk: {
|
|
if (condition) break :blk value;
|
|
break :blk other_value;
|
|
};
|
|
|
|
// Labeled loops
|
|
outer: for (rows) |row| {
|
|
for (row) |cell| {
|
|
if (cell == target) break :outer;
|
|
}
|
|
}
|
|
|
|
// continue with label
|
|
outer: for (items) |item| {
|
|
for (sub_items) |sub| {
|
|
if (skip) continue :outer;
|
|
}
|
|
}
|
|
```
|
|
|
|
### defer / errdefer
|
|
```zig
|
|
// Always runs when scope exits
|
|
fn example() void {
|
|
const resource = acquire();
|
|
defer release(resource); // runs on return
|
|
// use resource...
|
|
}
|
|
|
|
// Only runs on error return
|
|
fn example() !*Resource {
|
|
const ptr = try allocate();
|
|
errdefer free(ptr); // runs only if function returns error
|
|
try doSomething(ptr);
|
|
return ptr; // errdefer does NOT run
|
|
}
|
|
|
|
// errdefer with capture
|
|
fn example() !void {
|
|
errdefer |err| {
|
|
log.err("Failed with: {}", .{err});
|
|
};
|
|
try riskyOperation();
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Error Sets
|
|
```zig
|
|
// Define error set
|
|
const FileError = error{
|
|
NotFound,
|
|
AccessDenied,
|
|
OutOfMemory,
|
|
};
|
|
|
|
// Inferred error set (use sparingly)
|
|
fn foo() !void { ... } // error set inferred from body
|
|
|
|
// Merge error sets
|
|
const AllErrors = FileError || NetworkError;
|
|
|
|
// anyerror - global error set (avoid when possible)
|
|
fn bar() anyerror!void { ... }
|
|
```
|
|
|
|
### Error Unions
|
|
```zig
|
|
// Error union type: ErrorSet!PayloadType
|
|
fn parse(s: []const u8) ParseError!u32 { ... }
|
|
fn read() ![]u8 { ... } // inferred error set
|
|
|
|
// Return errors
|
|
return error.InvalidInput;
|
|
|
|
// Return success
|
|
return value;
|
|
```
|
|
|
|
### Handling Errors
|
|
```zig
|
|
// try - propagate error, unwrap on success
|
|
const value = try mayFail();
|
|
|
|
// catch - provide default on error
|
|
const value = mayFail() catch 0;
|
|
const value = mayFail() catch |err| {
|
|
log.err("failed: {}", .{err});
|
|
return default;
|
|
};
|
|
|
|
// catch unreachable - assert no error (crashes if error)
|
|
const value = mayFail() catch unreachable;
|
|
|
|
// if with error union
|
|
if (mayFail()) |value| {
|
|
// success
|
|
} else |err| {
|
|
// handle error
|
|
}
|
|
|
|
// switch on specific errors
|
|
mayFail() catch |err| switch (err) {
|
|
error.NotFound => return null,
|
|
error.AccessDenied => return error.PermissionDenied,
|
|
else => return err,
|
|
};
|
|
```
|
|
|
|
## Optionals
|
|
|
|
### Optional Types
|
|
```zig
|
|
// Optional type: ?T
|
|
var maybe: ?i32 = null;
|
|
maybe = 42;
|
|
|
|
// Check for null
|
|
if (maybe != null) { ... }
|
|
if (maybe == null) { ... }
|
|
```
|
|
|
|
### Unwrapping
|
|
```zig
|
|
// orelse - default value
|
|
const value = maybe orelse 0;
|
|
const value = maybe orelse return error.Missing;
|
|
const value = maybe orelse unreachable; // assert non-null
|
|
|
|
// .? - assert and unwrap (crashes on null)
|
|
const value = maybe.?;
|
|
|
|
// if unwrap
|
|
if (maybe) |value| {
|
|
// value is non-null
|
|
} else {
|
|
// was null
|
|
}
|
|
|
|
// while unwrap
|
|
while (iterator.next()) |item| { ... }
|
|
```
|
|
|
|
### Optional Pointers
|
|
```zig
|
|
// ?*T has null representation as 0 (same size as *T)
|
|
var ptr: ?*Node = null;
|
|
ptr = &node;
|
|
|
|
if (ptr) |p| {
|
|
p.*.data = 42;
|
|
}
|
|
```
|
|
|
|
## Structs
|
|
|
|
### Basic Structs
|
|
```zig
|
|
const Point = struct {
|
|
x: f32,
|
|
y: f32,
|
|
|
|
// Methods
|
|
pub fn distance(self: Point, other: Point) f32 {
|
|
const dx = self.x - other.x;
|
|
const dy = self.y - other.y;
|
|
return @sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
// Static method
|
|
pub fn origin() Point {
|
|
return .{ .x = 0, .y = 0 };
|
|
}
|
|
};
|
|
|
|
// Usage
|
|
const p = Point{ .x = 1.0, .y = 2.0 };
|
|
const p2: Point = .{ .x = 3.0, .y = 4.0 }; // type inferred
|
|
const dist = p.distance(p2);
|
|
```
|
|
|
|
### Default Values
|
|
```zig
|
|
const Config = struct {
|
|
name: []const u8,
|
|
port: u16 = 8080, // default value
|
|
debug: bool = false,
|
|
};
|
|
|
|
const cfg: Config = .{ .name = "server" }; // uses defaults
|
|
```
|
|
|
|
### @This() for Self-Reference
|
|
```zig
|
|
const Node = struct {
|
|
const Self = @This();
|
|
next: ?*Self = null,
|
|
data: i32,
|
|
|
|
pub fn append(self: *Self, node: *Self) void {
|
|
self.next = node;
|
|
}
|
|
};
|
|
```
|
|
|
|
### Packed Structs
|
|
```zig
|
|
const Flags = packed struct {
|
|
enabled: bool, // 1 bit
|
|
mode: u2, // 2 bits
|
|
_reserved: u5, // 5 bits
|
|
}; // Total: 1 byte
|
|
|
|
const flags: Flags = @bitCast(@as(u8, 0b10100001));
|
|
```
|
|
|
|
### Extern Structs (C ABI)
|
|
```zig
|
|
const CStruct = extern struct {
|
|
x: c_int,
|
|
y: c_int,
|
|
};
|
|
```
|
|
|
|
## Enums
|
|
|
|
### Basic Enums
|
|
```zig
|
|
const Color = enum {
|
|
red,
|
|
green,
|
|
blue,
|
|
};
|
|
|
|
const c: Color = .red;
|
|
|
|
// Switch (must be exhaustive)
|
|
switch (c) {
|
|
.red => {},
|
|
.green => {},
|
|
.blue => {},
|
|
}
|
|
```
|
|
|
|
### Enums with Values
|
|
```zig
|
|
const HttpStatus = enum(u16) {
|
|
ok = 200,
|
|
not_found = 404,
|
|
internal_error = 500,
|
|
_, // non-exhaustive marker
|
|
};
|
|
|
|
const code: u16 = @intFromEnum(HttpStatus.ok); // 200
|
|
const status: HttpStatus = @enumFromInt(404); // .not_found
|
|
```
|
|
|
|
### Enum Methods
|
|
```zig
|
|
const Direction = enum {
|
|
north,
|
|
south,
|
|
east,
|
|
west,
|
|
|
|
pub fn opposite(self: Direction) Direction {
|
|
return switch (self) {
|
|
.north => .south,
|
|
.south => .north,
|
|
.east => .west,
|
|
.west => .east,
|
|
};
|
|
}
|
|
};
|
|
```
|
|
|
|
## Unions
|
|
|
|
### Tagged Unions
|
|
```zig
|
|
const Value = union(enum) {
|
|
int: i64,
|
|
float: f64,
|
|
string: []const u8,
|
|
none, // void payload
|
|
|
|
pub fn isNumeric(self: Value) bool {
|
|
return switch (self) {
|
|
.int, .float => true,
|
|
else => false,
|
|
};
|
|
}
|
|
};
|
|
|
|
const v: Value = .{ .int = 42 };
|
|
|
|
switch (v) {
|
|
.int => |n| std.debug.print("{}", .{n}),
|
|
.float => |f| std.debug.print("{}", .{f}),
|
|
.string => |s| std.debug.print("{s}", .{s}),
|
|
.none => {},
|
|
}
|
|
```
|
|
|
|
### Bare Unions (no tag)
|
|
```zig
|
|
const Bare = union {
|
|
int: i32,
|
|
float: f32,
|
|
};
|
|
// Must track active field manually - unsafe
|
|
```
|
|
|
|
### Extern Unions (C ABI)
|
|
```zig
|
|
const CUnion = extern union {
|
|
as_int: c_int,
|
|
as_float: f32,
|
|
};
|
|
```
|
|
|
|
## Pointers and Slices
|
|
|
|
### Pointer Types
|
|
```zig
|
|
*T // single-item pointer
|
|
*const T // pointer to const
|
|
[*]T // many-item pointer (unknown length)
|
|
[*:0]T // null-terminated many-item pointer
|
|
?*T // optional pointer
|
|
|
|
// Alignment
|
|
*align(16) T // pointer with explicit alignment
|
|
```
|
|
|
|
### Slices
|
|
```zig
|
|
[]T // slice (pointer + length)
|
|
[]const T // slice to const data
|
|
[:0]T // null-terminated slice
|
|
|
|
// Create slice from array
|
|
const arr = [_]u8{ 1, 2, 3, 4, 5 };
|
|
const slice: []const u8 = &arr;
|
|
const sub: []const u8 = arr[1..4]; // {2, 3, 4}
|
|
|
|
// Slice operations
|
|
const len = slice.len;
|
|
const ptr = slice.ptr; // [*]const u8
|
|
const elem = slice[2];
|
|
```
|
|
|
|
### Pointer Arithmetic
|
|
```zig
|
|
// Many-item pointers support arithmetic
|
|
const ptr: [*]u8 = buffer.ptr;
|
|
const next = ptr + 1;
|
|
const offset = ptr + n;
|
|
|
|
// A single-item pointer can only establish a one-element slice by itself.
|
|
const one_ptr: *u8 = ...;
|
|
const one_item = one_ptr[0..1];
|
|
|
|
// Slicing an arbitrary length requires a many-item pointer plus an external
|
|
// guarantee that at least n elements are accessible.
|
|
const slice = ptr[0..n];
|
|
```
|
|
|
|
### Sentinel-Terminated
|
|
```zig
|
|
// Null-terminated string
|
|
const str: [:0]const u8 = "hello";
|
|
const c_str: [*:0]const u8 = str.ptr;
|
|
|
|
// Custom sentinel
|
|
const arr: [3:255]u8 = .{ 1, 2, 3 }; // followed by 255
|
|
```
|
|
|
|
## Comptime
|
|
|
|
### Comptime Variables
|
|
```zig
|
|
comptime var count: u32 = 0;
|
|
|
|
// Comptime block
|
|
comptime {
|
|
count += 1;
|
|
}
|
|
|
|
// Comptime parameter
|
|
fn repeat(comptime n: usize, value: u8) [n]u8 {
|
|
return [_]u8{value} ** n;
|
|
}
|
|
```
|
|
|
|
### Comptime Functions
|
|
```zig
|
|
fn factorial(comptime n: u32) u32 {
|
|
if (n == 0) return 1;
|
|
return n * factorial(n - 1);
|
|
}
|
|
|
|
const result = factorial(5); // computed at compile time
|
|
```
|
|
|
|
### Type as First-Class Value
|
|
```zig
|
|
fn Container(comptime T: type) type {
|
|
return struct {
|
|
items: []T,
|
|
|
|
pub fn get(self: @This(), i: usize) T {
|
|
return self.items[i];
|
|
}
|
|
};
|
|
}
|
|
|
|
const IntContainer = Container(i32);
|
|
```
|
|
|
|
### @typeInfo
|
|
```zig
|
|
fn isInteger(comptime T: type) bool {
|
|
return @typeInfo(T) == .int;
|
|
}
|
|
|
|
fn fieldNames(comptime T: type) *const [std.meta.fields(T).len][:0]const u8 {
|
|
// std.meta.fieldNames returns comptime-backed fixed storage.
|
|
return std.meta.fieldNames(T);
|
|
}
|
|
```
|
|
|
|
### inline for/while
|
|
```zig
|
|
// Unroll loop at compile time
|
|
inline for (0..4) |i| {
|
|
array[i] = computeValue(i);
|
|
}
|
|
|
|
// Generate code for each field
|
|
inline for (std.meta.fields(T)) |field| {
|
|
@field(value, field.name) = default;
|
|
}
|
|
```
|
|
|
|
## Functions
|
|
|
|
### Function Basics
|
|
```zig
|
|
fn add(a: i32, b: i32) i32 {
|
|
return a + b;
|
|
}
|
|
|
|
// With error
|
|
fn parse(s: []const u8) !i32 { ... }
|
|
|
|
// Void return
|
|
fn log(msg: []const u8) void { ... }
|
|
|
|
// Noreturn
|
|
fn abort() noreturn {
|
|
@panic("aborted");
|
|
}
|
|
```
|
|
|
|
### Generic Functions
|
|
```zig
|
|
fn max(comptime T: type, a: T, b: T) T {
|
|
return if (a > b) a else b;
|
|
}
|
|
|
|
// Using anytype
|
|
fn print(value: anytype) void {
|
|
const T = @TypeOf(value);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Function Pointers
|
|
```zig
|
|
const BinaryOp = *const fn (i32, i32) i32;
|
|
|
|
fn apply(op: BinaryOp, a: i32, b: i32) i32 {
|
|
return op(a, b);
|
|
}
|
|
```
|
|
|
|
### Calling Conventions
|
|
```zig
|
|
fn cFunc() callconv(.c) void { ... }
|
|
fn nakedFunc() callconv(.naked) noreturn { ... }
|
|
fn inlineFunc() callconv(.@"inline") i32 { ... }
|
|
```
|
|
|
|
### Export/Extern
|
|
```zig
|
|
// Export to C
|
|
export fn my_function() void { ... }
|
|
|
|
// Import from C
|
|
extern "c" fn printf(fmt: [*:0]const u8, ...) c_int;
|
|
|
|
// Link with library
|
|
extern "SDL2" fn SDL_Init(flags: u32) c_int;
|
|
```
|
|
|
|
### Inline Functions
|
|
```zig
|
|
inline fn fastAdd(a: i32, b: i32) i32 {
|
|
return a + b;
|
|
}
|
|
// Forces inlining - compile error if impossible
|
|
```
|