Skip to content

Zig Basics — Variables, Functions, Control Flow, and Memory

DodaTech Updated 2026-06-29 3 min read

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

  1. Write a function that checks if a number is prime.

  2. Use a for loop to sum all elements of an array.

  3. Write a switch expression that maps month numbers to month names.

  4. Create a function that takes a slice and returns its maximum element.

  5. Use @intCast to safely convert between different integer sizes.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro