Basics
Operators
-
List .
Keywords
Access Modifiers
-
Chat-gpt: " Default: There is no native visibility control."
-
pubis related to code organization and modularity, but is not tied to dynamic runtime security rules . -
Zig uses modular organization to control access between different parts of the code.
-
Public (
pub) : Used to expose functions, types or variables from a module to other modules. -
Private (default) : Items defined without
pubare private to the current scope (file or block)."
-
Comments, Print, Formatting
Comments
//! Top-level documentation.
/// Documentation comment.
// Simple comment.
Suppress Warnings
//suppress unused constant compile error
_ = .{ a, b, c, d };
-
No automatic
\n. -
"Prints to stderr (it's a shortcut based on
std.io.getStdErr())". -
Part of the standard library (
std). -
Uses
std.debug.print(). -
Writes directly to standard output (
stdout). -
Accepts string formatting, similar to
printfin C.
const std = @import("std");
const print = std.debug.print
pub fn main() void {
print("Value: {}\n", .{42});
}
Info
-
With automatic
\n. -
Part of the
std.logmodule. -
Uses
std.log.info(). -
Unlike
print,infocan be filtered by log levels (likedebug,warn,err). -
Output can be redirected or configured depending on the compiler and runtime environment.
const std = @import("std");
pub fn main() void {
std.log.info("Value: {}", .{42});
}
Formatting
-
std.fmtprovides ways to format data to and from strings. -
A basic example of creating a formatted string. The format string must be compile-time known. The
dhere denotes that we want a decimal number.
const std = @import("std");
const expect = std.testing.expect;
const eql = std.mem.eql;
const test_allocator = std.testing.allocator;
test "fmt" {
const string = try std.fmt.allocPrint(
test_allocator,
"{d} + {d} = {d}",
.{ 9, 10, 19 },
);
defer test_allocator.free(string);
try expect(eql(u8, string, "9 + 10 = 19"));
}
-
std.debug.print :
-
"it writes to stderr and is protected by a mutex."
const std = @import("std"); const expect = std.testing.expect; const eql = std.mem.eql; test "hello world" { const out_file = std.io.getStdOut(); try out_file.writer().print( "Hello, {s}!\n", .{"World"}, ); } -
-
Format Specifiers :
-
{s}: strings. -
{d}: decimal.-
{d:.2}
-
-
{c}: ascii character. -
{*}: pointer formatting, printing the address rather than the value. -
{any}: default formatting. -
{e}: floats in scientific notation. -
{b}: binary. -
{o}: octal. -
etc.
-
Functions
-
All function arguments are immutable - if a copy is desired the user must explicitly make one.
-
Unlike variables, which are snake_case, functions are camelCase.
fn addFive(x: u32) u32 {
return x + 5;
}
test "function" {
const y = addFive(0);
}
Built-in Functions
Control Flow (if, while, for, switch, labelled, iterators)
If
const a = true;
var x: u16 = 0;
if (a) {
x += 1;
} else {
x += 2;
}
const a = true;
var x: u16 = 0;
x += if (a) 1 else 2;
-
"If it exists" :
// Shortcut for "if (x) x else 0" var value = x orelse 0;// Get a pointer to the value (if it exists). if (a) |*value| { value.* += 1; }
While
var i: u8 = 2;
while (i < 100) {
i *= 2;
}
// Simple "while" loop.
while (i < 10) { i += 1; }
// While loop with a "continue expression"
// (expression executed as the last expression of the loop).
while (i < 10) : (i += 1) { ... }
// Same, with a more complex continue expression (block of code).
while (i * j < 2000) : ({ i *= 2; j *= 3; }) { ... }
var sum: u8 = 0;
var i: u8 = 1;
while (i <= 10) : (i += 1) {
sum += i;
}
var sum: u8 = 0;
var i: u8 = 0;
while (i <= 3) : (i += 1) {
if (i == 2) continue;
sum += i;
}
var sum: u8 = 0;
var i: u8 = 0;
while (i <= 3) : (i += 1) {
if (i == 2) break;
sum += i;
}
-
Loops as Expressions :
-
Like
return,breakaccepts a value. -
This can be used to yield a value from a loop.
-
Loops in Zig also have an
elsebranch, which is evaluated when the loop is not exited with abreak.
fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { var i = begin; return while (i < end) : (i += 1) { if (i == number) { break true; } } else false; } test "while loop expression" { try expect(rangeHasNumber(0, 10, 3)); } -
For
// We've had to assign values to `_`, as Zig does not allow us to have unused values.
// Character literals are equivalent to integer literals
const string = [_]u8{ 'a', 'b', 'c' };
for (string, 0..) |character, index| {
_ = character;
_ = index;
}
for (string) |character| {
_ = character;
}
for (string, 0..) |_, index| {
_ = index;
}
for (string) |_| {}
// To iterate over a portion of a slice, reslice.
for (items[0..1]) |value| { sum += value; }
// Loop over every item of an array (or slice).
for (items) |value| { sum += value; }
// Iterate and get pointers on values instead of copies.
for (items) |*value| { value.* += 1; }
// Iterate with an index.
for (items) |value, i| { print("val[{}] = {}\n", .{i, value}); }
// Iterate with pointer and index.
for (items) |*value, i| { print("val[{}] = {}\n", .{i, value}); value.* += 1; }
// Break and continue are supported.
for (items) |value| {
if (value == 0) { continue; }
if (value >= 10) { break; }
// ...
}
// For loops can also be used as expressions.
// Similar to while loops, when you break from a for loop,
// the else branch is not evaluated.
var sum: i32 = 0;
// The "for" loop has to provide a value, which will be the "else" value.
const result = for (items) |value| {
if (value != null) {
sum += value.?; // "result" will be the last "sum" value.
}
} else 0; // Last value.
Switch
-
Safety :
-
The types of all branches must coerce to the type which is being switched upon. All possible values must have an associated branch - values cannot be left out. It is exhaustive.
-
-
Zig's
switchworks as both a statement and an expression.-
Statement :
const expect = @import("std").testing.expect; test "switch statement" { var x: i8 = 10; switch (x) { -1...1 => { x = -x; }, 10, 100 => { //special considerations must be made //when dividing signed integers x = @divExact(x, 10); }, else => {}, } try expect(x == 1); } -
Expression :
const expect = @import("std").testing.expect; test "switch expression" { var x: i8 = 10; x = switch (x) { -1...1 => -x, 10, 100 => @divExact(x, 10), else => x, }; try expect(x == 1); }
-
-
Cases cannot fall through to other branches.
Labelled
-
Blocks
-
The value of an empty block
{}is a value of the typevoid.
const expect = @import("std").testing.expect; test "int-float conversion" { const a: i32 = 0; const b = @as(f32, @floatFromInt(a)); const c = @as(i32, @intFromFloat(b)); try expect(c == a); }-
This can be seen as being equivalent to C's
i++.
blk: { const tmp = i; i += 1; break :blk tmp; } -
-
Loops :
-
Loops can be given labels, allowing you to
breakandcontinueto outer loops.
test "nested continue" { var count: usize = 0; outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { count += 1; continue :outer; } } try expect(count == 8); } -
Iterators
Imports
-
The built-in function
@importtakes in a file, and gives you a struct type based on that file. -
All declarations labelled as
pub(for public) will end up in this struct type, ready for use. -
@import("std")is a special case in the compiler, and gives you access to the standard library.