const std = @import("std");
pub fn main() !void {
try std.fs.File.stdout().writeAll("Hello world!\n");
}
// Immutable variable (preferred)
const x: i32 = 42;
// Mutable variable
var y: i32 = 10;
y = 20;
// Type inference
const z = 100; // inferred as comptime_int
// Undefined (uninitialized)
var buffer: [100]u8 = undefined;
// Integers
const a: i8 = -128; // signed 8-bit
const b: u32 = 1000; // unsigned 32-bit
const c: usize = 42; // pointer-sized unsigned
// Floats
const f: f32 = 3.14;
const d: f64 = 2.71828;
// Boolean
const flag: bool = true;
// Optional
const maybe: ?i32 = null;
const value: ?i32 = 42;
// Error union
const result: anyerror!i32 = 42;
// Fixed-size array
const arr = [_]i32{ 1, 2, 3, 4, 5 };
// Access elements
const first = arr[0];
// Array length
const len = arr.len;
// Slice (pointer + length)
const slice: []const i32 = arr[1..4];
// Iterate over array
for (arr) |item| {
// use item
}
// With index
for (arr, 0..) |item, index| {
// use item and index
}
// String literals are []const u8
const hello = "Hello, World!";
// Multiline strings
const multiline =
\\Line 1
\\Line 2
\\Line 3
;
// String concatenation (comptime)
const greeting = "Hello, " ++ "Zig!";
// Format strings
const std = @import("std");
var buf: [100]u8 = undefined;
const formatted = std.fmt.bufPrint(&buf, "Value: {d}", .{42});
// Basic function
fn add(a: i32, b: i32) i32 {
return a + b;
}
// Function with error return
fn divide(a: f64, b: f64) !f64 {
if (b == 0) return error.DivisionByZero;
return a / b;
}
// Generic function
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
// Inline function
inline fn square(x: i32) i32 {
return x * x;
}
// If expression
const result = if (x > 0) "positive" else "non-positive";
// If statement
if (condition) {
// do something
} else if (other_condition) {
// do something else
} else {
// default
}
// Switch expression
const output = switch (value) {
1 => "one",
2, 3 => "two or three",
4...10 => "four to ten",
else => "other",
};
// While loop
while (condition) {
// loop body
}
// While with continue expression
var i: usize = 0;
while (i < 10) : (i += 1) {
// loop body
}
// Define a struct
const Point = struct {
x: f64,
y: f64,
// Method
pub fn distance(self: Point, other: Point) f64 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return @sqrt(dx * dx + dy * dy);
}
// Associated function
pub fn origin() Point {
return Point{ .x = 0, .y = 0 };
}
};
// Create instance
const p = Point{ .x = 3.0, .y = 4.0 };
// Access fields
const x_val = p.x;
// Call method
const dist = p.distance(Point.origin());
// Basic enum
const Direction = enum {
north,
south,
east,
west,
};
// Enum with values
const HttpStatus = enum(u16) {
ok = 200,
not_found = 404,
internal_error = 500,
};
// Tagged union
const Value = union(enum) {
int: i32,
float: f64,
string: []const u8,
none,
};
const v = Value{ .int = 42 };
switch (v) {
.int => |i| // use i,
.float => |f| // use f,
else => {},
}
// Define errors
const FileError = error{
NotFound,
PermissionDenied,
EndOfFile,
};
// Return error
fn readFile(path: []const u8) FileError![]u8 {
if (path.len == 0) return error.NotFound;
// ...
}
// Try operator (propagate error)
const data = try readFile("test.txt");
// Catch with default
const result = readFile("test.txt") catch "default";
// Catch with handler
const value = readFile("test.txt") catch |err| {
std.debug.print("Error: {}\n", .{err});
return err;
};
// If error capture
if (readFile("test.txt")) |data| {
// success
} else |err| {
// handle error
}
// Optional type
var maybe_value: ?i32 = null;
maybe_value = 42;
// Unwrap with orelse
const value = maybe_value orelse 0;
// Unwrap with if
if (maybe_value) |v| {
// use v
}
// Optional pointer
const ptr: ?*i32 = null;
if (ptr) |p| {
p.* = 10;
}
| Allocator | Use Case | Safety | Performance | Notes |
|---|
GeneralPurposeAllocator | Production code | High | Good | Detects memory leaks and double-frees |
ArenaAllocator | Temporary/batch allocations | High | Excellent | Free all at once, no individual frees |
FixedBufferAllocator | Stack/embedded systems | Medium | Excellent | Fixed-size buffer, no dynamic allocation |
page_allocator | Large allocations | Medium | Good | Direct OS memory pages (slow for small allocs) |
c_allocator | C interop | Low | Good | Wraps malloc/free |
const std = @import("std");
// GeneralPurposeAllocator (recommended for most use cases)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); // Check for leaks
const allocator = gpa.allocator();
// Allocate single item
const ptr = try allocator.create(i32);
defer allocator.destroy(ptr);
ptr.* = 42;
// Allocate slice
const slice = try allocator.alloc(u8, 100);
defer allocator.free(slice);
// Arena - allocate many items, free all at once
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Frees everything
const arena_allocator = arena.allocator();
// No need for individual frees
const items = try arena_allocator.alloc(i32, 100);
const more = try arena_allocator.create(MyStruct);
// All freed with arena.deinit()
// Fixed buffer (stack allocation)
var buffer: [1024]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const fba_allocator = fba.allocator();
// Limited to buffer size, no heap allocation
const data = try fba_allocator.alloc(u8, 100);
// ArrayList
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
// HashMap
var map = std.AutoHashMap([]const u8, i32).init(allocator);
defer map.deinit();
try map.put("key", 42);
const std = @import("std");
// Read entire file into memory
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const data = try std.fs.cwd().readFileAlloc(allocator, "./some_file.txt", 1024 * 1024);
defer allocator.free(data);
std.debug.print("File Contents: {s}\n", .{data});
const std = @import("std");
const file = try std.fs.cwd().createFile("./some-file.txt", .{});
defer file.close();
try file.writeAll("Hello, Zig File I/O!\n");
const std = @import("std");
pub fn main() !void {
try std.fs.File.stdout().writeAll("Enter your name: ");
// Get input from stdin
var stdin_buf: [256]u8 = undefined;
var stdin_reader = std.fs.File.stdin().reader(&stdin_buf);
const stdin = &stdin_reader.interface;
const name = try stdin.takeDelimiter('\n');
// Display output to stdout
var stdout_buf: [256]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
const stdout = &stdout_writer.interface;
try stdout.print("Hello, {s}", .{name orelse ""});
try stdout.flush();
}
// Compile-time evaluation
const computed = comptime blk: {
var result: i32 = 0;
for (0..10) |i| {
result += @intCast(i);
}
break :blk result;
};
// Comptime parameter
fn repeat(comptime n: usize, value: u8) [n]u8 {
return [_]u8{value} ** n;
}
// Type reflection
fn debugPrint(comptime T: type, value: T) void {
const info = @typeInfo(T);
// use type info
}
// Single-item pointer
var x: i32 = 42;
const ptr: *i32 = &x;
ptr.* = 100; // dereference
// Const pointer
const const_ptr: *const i32 = &x;
// Many-item pointer
const arr = [_]i32{ 1, 2, 3 };
const many_ptr: [*]const i32 = &arr;
// Pointer arithmetic
const second = many_ptr + 1;
// Sentinel-terminated pointer
const str: [*:0]const u8 = "hello";
const std = @import("std");
const expect = std.testing.expect;
test "basic test" {
const x = 1 + 1;
try expect(x == 2);
}
test "string equality" {
const a = "hello";
const b = "hello";
try expect(std.mem.eql(u8, a, b));
}
// Run tests: zig test filename.zig
// Standard library import
const std = @import("std");
// Local file import
const utils = @import("utils.zig");
// Access public declarations
const result = utils.helperFunction();
// Built-in functions
const size = @sizeOf(i32); // size in bytes
const aligned = @alignOf(i64); // alignment
const type_name = @typeName(i32); // "i32"