Zig Basics — Variables, Functions, Control Flow, and Memory
In this tutorial, you will learn about Zig Basics. We cover key concepts, practical examples, and best practices to help you master this topic.
Zig is a systems programming language focused on simplicity, performance, and explicitness. It has no hidden memory allocation, no operator overloading, no hidden control flow, and no preprocessor — what you see is what you get.
In this tutorial, you'll learn Zig's syntax and philosophy. Zig is used at DodaTech for low-level security tools where C is dangerous and Rust's borrow checker adds complexity.
What You'll Learn
- Installing Zig
- Variables (const vs var)
- Functions and parameters
- Control flow (if, for, while, switch)
- Integers and numeric types
- Arrays and slices
- Hello World in Zig
Installing Zig
# Download from https://ziglang.org/download/
# Or use package manager:
# apt install zig
# brew install zig
zig version # Verify installation
Hello World
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, Zig!
", .{});
}
// Compile and run:
// zig run hello.zig
// or:
// zig build-exe hello.zig && ./hello
Variables
// Immutable (runtime constant)
const x: i32 = 42;
// x = 43; // Error: cannot assign to constant
// Mutable
var y: i32 = 10;
y = 20;
// Type inference
const z = 3.14; // comptime_float
const name = "Alice"; // *const [5:0]u8 (sentinel-terminated array)
// Multiple declarations
const a, var b = .{ 1, 2 };
// a is 1 (immutable), b is 2 (mutable)
Integer Types
// Explicitly-sized integers
const a: i8 = -128; // 8-bit signed
const b: u8 = 255; // 8-bit unsigned
const c: i16 = 32767; // 16-bit signed
const d: u64 = 18446744073709551615; // 64-bit unsigned
// Arbitrary-width integers
const e: u7 = 127; // 7-bit unsigned
// Integer operations
const sum = @as(u32, 10) + @as(u32, 20);
const wrapped = @addWithOverflow(u8, 255, 1); // { 0, true }
// Wrapping, saturating arithmetic
const wrap = @intCast(u8, 256); // Runtime safety check
const sat: u8 = 200 +| 100; // Saturating add => 255
Functions
// Basic function
fn add(a: i32, b: i32) i32 {
return a + b;
}
// Void function
fn greet(name: []const u8) void {
std.debug.print("Hello, {s}!
", .{name});
}
// Public function (visible outside module)
pub fn square(x: i32) i32 {
return x * x;
}
// Inline return type inference
fn max(a: i32, b: i32) @TypeOf(a, b) {
return if (a > b) a else b;
}
Control Flow
// if/else (is an expression)
const score = 85;
const grade = if (score >= 90) "A"
else if (score >= 80) "B"
else if (score >= 70) "C"
else "F";
// for loop (over arrays/slices)
const items = [_]i32{ 10, 20, 30, 40 };
for (items) |item| {
std.debug.print("{}
", .{item});
}
// for with index
for (items, 0..) |item, index| {
std.debug.print("{}: {}
", .{ index, item });
}
// while loop
var i: usize = 0;
while (i < 5) : (i += 1) {
std.debug.print("{}
", .{i});
}
// switch
const status: u8 = 404;
const msg = switch (status) {
200 => "OK",
201 => "Created",
404 => "Not Found",
500 => "Server Error",
else => "Unknown",
};
Arrays and Slices
// Fixed-size array
const arr = [_]i32{ 1, 2, 3, 4 };
const first = arr[0]; // 1
// Slice (pointer + length)
const slice: []const i32 = arr[1..3]; // [2, 3]
const full_slice: []const i32 = arr[0..arr.len];
// Multi-dimensional arrays
const matrix = [3][3]i32{
.{ 1, 0, 0 },
.{ 0, 1, 0 },
.{ 0, 0, 1 },
};
Practice Questions
Write a function that checks if a number is prime.
Use a for loop to sum all elements of an array.
Write a switch expression that maps month numbers to month names.
Create a function that takes a slice and returns its maximum element.
Use @intCast to safely convert between different integer sizes.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro