"Zig Systems Project" — design and implement a complete systems-level application in Zig (e.g., a CLI tool, HTTP server, or memory-efficient data processor). Include proper memory management, error handling, tests, and documentation.
🎯 portfolio piece · peer review · certification exam
“Certified Zig Programming Expert” – Master the language of modern systems programming
Welcome, young programmer! Have you ever wondered how computer programs are made? Programs are written in special languages called programming languages. There are many languages, like Python, JavaScript, and C. Today, we are going to learn a very special language called Zig.
Zig is a modern programming language that is simple, fast, and safe. It is used to build operating systems, games, servers, and many other important programs. Zig was created to fix problems in older languages like C. It gives you control over your computer while keeping you safe from common mistakes.
In this module, we will start from the very beginning. You don't need any experience with programming. We will learn what Zig is, how to set it up, and how to write your first programs. We will learn about variables, data types, control flow, functions, structs, and more. By the end of this module, you will be able to write simple Zig programs with confidence.
Let's begin!
After finishing this module, you will be able to:
Chidi is 13 years old and lives in Lagos. He loves computers and wants to learn how to make his own programs. One day, his uncle, who is a software engineer, told him about a new language called Zig.
“Zig is amazing,” his uncle said. “It is simple, fast, and safe. It lets you build things that are very close to the computer's heart.” Chidi was curious. “Can I learn it?” he asked. “Of course!” his uncle replied. “Let's start today.”
Chidi’s uncle helped him install Zig on his computer. They opened a code editor and wrote a simple program. It printed the words “Hello, Zig!” on the screen. Chidi was so happy. He had written his first program!
Next, they learned about variables. Chidi created a variable called “name” and put his name in it. He created another variable called “age” and put his age in it. Then they learned about functions, structs, and arrays. Every new idea was like a new toy to play with.
By the end of the day, Chidi had written five small programs. He felt like a real programmer. “Zig is fun!” he said. His uncle smiled. “This is just the beginning. Keep learning, and you will build amazing things.”
Moral of the story: Programming is like learning a new language. Zig is a simple, powerful language that anyone can learn. Start with small steps, and you will go far.
Definition: Zig is a modern programming language used to build fast, safe, and reliable software.
Why it is important: Zig is designed to fix problems in older languages and make programming simpler.
Simple explanation: Imagine you have a toolbox. Some tools are old and rusty. Zig is a new, shiny tool that works better.
Real-life example: Companies use Zig to build operating systems, web servers, and games.
School example: A student uses Zig to build a simple calculator program.
Home example: A family uses a Zig program to organize their shopping list.
Nigerian example: A Nigerian startup uses Zig to build a fast payment system.
Illustration:
Zig = Modern Programming Language
|
V
Simple, Fast, Safe
|
V
Used for:
- Operating systems
- Web servers
- Games
- Tools
Mini summary: Zig is a modern programming language. It is simple, fast, and safe. It is used to build many kinds of software.
Definition: Learning Zig means you can build powerful programs that run very fast.
Why it is important: Zig gives you control and safety at the same time.
Simple explanation: Imagine driving a car that is both fast and safe. That is Zig.
Real-life example: Game developers use Zig for high-performance games.
School example: Students learn Zig to understand how computers really work.
Home example: Hobbyists use Zig to build small tools and games.
Nigerian example: Nigerian developers use Zig for efficient software.
Illustration:
Why Learn Zig? +---------------------+ | Fast | +---------------------+ | Safe | +---------------------+ | Simple | +---------------------+ | Powerful | +---------------------+ | Modern | +---------------------+
Mini summary: Zig is fast, safe, simple, and powerful. It is a great language to learn.
Definition: Installing Zig means putting the Zig compiler on your computer.
Why it is important: You need the compiler to turn your code into a program.
Simple explanation: Like installing a new app on your phone before you can use it.
Real-life example: Developers install Zig to build software.
School example: Students install Zig on school computers.
Home example: Families install Zig on home computers.
Nigerian example: Nigerian developers install Zig to build tools.
Illustration:
Installing Zig: 1. Go to ziglang.org 2. Download the version for your computer 3. Extract the file 4. Add Zig to your PATH 5. Open a terminal and type: zig version 6. You should see the version number 🎉
Step-by-step:
zig version.Mini summary: Install Zig by downloading it and adding it to your PATH. Check with zig version.
Definition: A Zig program is a set of instructions that tells the computer what to do.
Why it is important: Writing your first program is the start of your journey.
Simple explanation: Like writing your first sentence in a new language.
Real-life example: Programmers start with “Hello, World!”
School example: Students write “Hello, World!” in class.
Home example: Families write simple programs for fun.
Nigerian example: Nigerian students write “Hello, Nigeria!”
Illustration:
hello.zig:
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, Zig!\n", .{});
}
Run it:
zig run hello.zig
Output:
Hello, Zig!
Step-by-step:
hello.zig.zig run hello.zig.Mini summary: Your first Zig program prints “Hello, Zig!” to the screen. Run it with zig run.
Definition: A variable is a box that holds a value that can change. A constant holds a value that cannot change.
Why it is important: Variables and constants let you store and use data.
Simple explanation: Imagine a labeled box. You can put things in and take them out (variable). A sealed box cannot be changed (constant).
Real-life example: A bank stores your balance in a variable.
School example: A teacher stores a student's score in a variable.
Home example: A family stores the number of eggs in a variable.
Nigerian example: A trader stores the price of rice in a variable.
Illustration:
Variables and Constants: var age: u8 = 13; // can change const pi: f32 = 3.14; // cannot change age = 14; // OK pi = 3.15; // ERROR
Step-by-step:
var to create a variable.const to create a constant.u8 for small numbers).=.Mini summary: Variables can change; constants cannot. Use var and const.
Definition: Data types describe what kind of value a variable holds: numbers, text, true/false, etc.
Why it is important: Knowing data types helps you store data correctly.
Simple explanation: Like different containers for different things: bottles for water, boxes for books.
Real-life example: Banks use numbers for money and text for names.
School example: Teachers use numbers for scores and text for names.
Home example: Families use numbers for ages and text for addresses.
Nigerian example: Businesses use numbers for prices and text for product names.
Illustration:
Common Zig Types: +----------+---------------------------+ | Type | What It Holds | +----------+---------------------------+ | u8 | Small positive number | | i32 | Whole number (positive/neg)| | f64 | Decimal number | | bool | true or false | | []u8 | Text (string) | +----------+---------------------------+
Mini summary: Zig has many data types: numbers (u8, i32, f64), booleans (bool), and text ([]u8).
Definition: Control flow lets your program make decisions.
Why it is important: Decisions make programs smart.
Simple explanation: Like choosing what to wear based on the weather.
Real-life example: A bank checks if you have enough money before you withdraw.
School example: A teacher gives a pass or fail based on a score.
Home example: A family decides to buy food based on the budget.
Nigerian example: A trader decides to sell based on the price.
Illustration:
if statement:
const score: u8 = 85;
if (score >= 70) {
std.debug.print("Pass!\n", .{});
} else {
std.debug.print("Fail.\n", .{});
}
switch statement:
const day: u8 = 3;
switch (day) {
1 => std.debug.print("Monday\n", .{}),
2 => std.debug.print("Tuesday\n", .{}),
3 => std.debug.print("Wednesday\n", .{}),
else => std.debug.print("Other day\n", .{}),
}
Step-by-step:
if to test a condition.else for the other case.switch to choose between many options.Mini summary: Use if and switch to make decisions in your program.
Definition: Loops let you repeat a block of code many times.
Why it is important: Loops save time and let you process lists.
Simple explanation: Like doing push-ups 10 times in a row.
Real-life example: A bank processes many transactions in a loop.
School example: A teacher adds up the scores of all students.
Home example: A family counts all items in a shopping list.
Nigerian example: A trader calculates total sales for the day.
Illustration:
while loop:
var i: u8 = 1;
while (i <= 5) : (i += 1) {
std.debug.print("{d}\n", .{i});
}
for loop:
const numbers = [_]u8{10, 20, 30};
for (numbers) |n| {
std.debug.print("{d}\n", .{n});
}
Step-by-step:
while to repeat while a condition is true.for to go through a list of items.Mini summary: Use while and for to repeat code. Loops are powerful tools.
Definition: A function is a block of code that does a specific job and can be reused.
Why it is important: Functions make programs cleaner and easier to reuse.
Simple explanation: Like a recipe you can use again and again.
Real-life example: A bank uses a function to calculate interest.
School example: A teacher uses a function to calculate averages.
Home example: A family uses a function to calculate budget totals.
Nigerian example: A trader uses a function to calculate prices.
Illustration:
Function Example:
fn add(a: i32, b: i32) i32 {
return a + b;
}
pub fn main() void {
const result = add(3, 4);
std.debug.print("Sum: {d}\n", .{result});
}
Step-by-step:
fn to start a function.return.Mini summary: Functions are reusable blocks of code. Use fn to define them and call them by name.
Definition: A struct groups related data together. An enum is a list of named values.
Why it is important: They help you organize your data clearly.
Simple explanation: A struct is like a bag with labeled pockets. An enum is like a list of choices on a menu.
Real-life example: A bank uses structs for customer info and enums for account types.
School example: A teacher uses structs for student info and enums for grades.
Home example: A family uses structs for family members and enums for roles.
Nigerian example: A trader uses structs for products and enums for categories.
Illustration:
Struct and Enum:
const Color = enum { red, green, blue };
const Person = struct {
name: []const u8,
age: u8,
favorite: Color,
};
const ada = Person{
.name = "Ada",
.age = 13,
.favorite = .green,
};
Step-by-step:
struct to group related data.enum to list named values.ada.name.Mini summary: Structs group data; enums list named values. They make your code organized and clear.
Definition: An array is a fixed-size list. A slice is a view into a part of an array.
Why it is important: Arrays and slices let you work with many values at once.
Simple explanation: An array is like a row of boxes. A slice points to some of those boxes.
Real-life example: A bank uses an array for transaction history.
School example: A teacher uses an array for class scores.
Home example: A family uses an array for shopping items.
Nigerian example: A trader uses an array for daily sales.
Illustration:
Array and Slice:
const numbers = [_]u8{1, 2, 3, 4, 5}; // array
const part = numbers[1..4]; // slice
for (part) |n| {
std.debug.print("{d}\n", .{n});
}
Step-by-step:
[_]type{values}.array[start..end].for to loop through them.Mini summary: Arrays hold fixed lists; slices point to parts of lists. Both are used with loops.
Definition: A string is a list of characters. In Zig, strings are slices of bytes: []const u8.
Why it is important: Strings let you work with text.
Simple explanation: Like writing a sentence using letters.
Real-life example: Banks store customer names as strings.
School example: Teachers store student names as strings.
Home example: Families store addresses as strings.
Nigerian example: Businesses store product names as strings.
Illustration:
String Example:
const name: []const u8 = "Ada";
std.debug.print("Hello, {s}!\n", .{name});
Output:
Hello, Ada!
Step-by-step:
const name = "text";.{s} in print to show it.Mini summary: Strings are slices of bytes. Use {s} to print them.
Definition: Compiling means turning your code into a program the computer can run.
Why it is important: You need to compile before you can run your program.
Simple explanation: Like baking a cake. You mix ingredients (write code), then bake (compile), then eat (run).
Real-life example: Developers compile their code into apps.
School example: Students compile their first program.
Home example: Families compile small tools.
Nigerian example: Businesses compile their software.
Illustration:
Compile and Run: zig build-exe hello.zig // compile ./hello // run (Linux/Mac) hello.exe // run (Windows) Or simply: zig run hello.zig // compile and run
Step-by-step:
zig run hello.zig.zig build-exe to create an executable.Mini summary: Use zig run to compile and run in one step, or zig build-exe to create a program.
Definition: Mistakes happen. Knowing them helps you avoid them.
Why it is important: A small mistake can stop your program.
Simple explanation: Like a spelling mistake in a sentence.
Real-life example: Developers use tools to find mistakes.
School example: Students learn from errors in their code.
Home example: Families check their code twice.
Nigerian example: Businesses test their code carefully.
Table of common mistakes:
| Mistake | What Happens | How to Fix |
|---|---|---|
| Missing semicolon | Error message | Add ; at the end |
| Wrong type | Error message | Use the correct type |
Forgetting const/var | Error message | Add the keyword |
| Unused variable | Warning/error | Remove or use it |
| Forgetting imports | Unknown name | Add @import |
| Mismatched types | Error | Convert types properly |
Mini summary: Common mistakes include missing semicolons and wrong types. Read error messages carefully.
Definition: Best practices are good habits that make your code clean and safe.
Why it is important: Good habits prevent bugs and save time.
Simple explanation: Like keeping your room tidy. It is easier to find things.
Real-life example: Developers write clean code for their teams.
School example: Students write neat code for projects.
Home example: Families keep their tools organized.
Nigerian example: Businesses write clear code for maintenance.
List of best practices:
const by default.defer to clean up resources.zig fmt.Mini summary: Best practices: use const, clear names, short functions, handle errors, test often.
| Word | Simple Definition |
|---|---|
| Zig | A modern programming language. |
| Compiler | A tool that turns code into a program. |
| Variable | A box that holds a value that can change. |
| Constant | A box that holds a value that cannot change. |
| Data type | What kind of value a variable holds. |
| Function | A reusable block of code. |
| Struct | A group of related data. |
| Enum | A list of named values. |
| Array | A fixed-size list. |
| Slice | A view into part of an array. |
| String | A list of characters. |
| Loop | Code that repeats. |
| Condition | A test that is true or false. |
| Parameter | An input to a function. |
| Return | The result a function gives back. |
zig version.var and const.fn.zig run.zig version.hello.zig.zig run hello.zig.var or const.=.;.fn.return.while or for.comptime for compile-time code.try and catch for errors?std?var for variables and const for constants.if, switch, while, and for for control flow.zig run.var or const.const by default.defer for cleanup.zig fmt.
hello.zig
+---------------------------+
| @import("std") |
+---------------------------+
| pub fn main() void { |
| print("Hello, Zig!") |
| } |
+---------------------------+
var age: u8 = 13; // Can change const pi: f32 = 3.14; // Cannot change
if (score >= 70) {
Pass
} else {
Fail
}
while (i <= 5) : (i += 1) {
print(i)
}
Input → Function → Output
3, 4 add() 7
Module 1: Zig Basics
|
V
Module 2: Memory & Errors
|
V
Module 3: Advanced Features
|
V
Module 4: Real Projects
|
V
Zig Expert 🎉
| Feature | Zig | Python | C |
|---|---|---|---|
| Speed | Very Fast | Slow | Very Fast |
| Safety | High | High | Low |
| Memory | Manual | Automatic | Manual |
| Learning | Medium | Easy | Hard |
| Feature | var | const |
|---|---|---|
| Can change? | Yes | No |
| Use for | Changing values | Fixed values |
| Best practice | Only when needed | Default choice |
| Feature | Array | Slice |
|---|---|---|
| Size | Fixed | Can vary |
| Owns data | Yes | No |
| Best for | Fixed lists | Passing parts |
| Feature | Struct | Enum |
|---|---|---|
| Purpose | Group data | List choices |
| Example | Person { name, age } | Color { red, green } |
| Use | Complex objects | Named values |
Lesson 1: Zig is a modern programming language.
Lesson 2: Zig is fast, safe, simple, and powerful.
Lesson 3: Install Zig from ziglang.org and check with zig version.
Lesson 4: Your first program prints “Hello, Zig!”.
Lesson 5: Use var for variables and const for constants.
Lesson 6: Zig types include u8, i32, f64, bool, []u8.
Lesson 7: Use if and switch for decisions.
Lesson 8: Use while and for for loops.
Lesson 9: Functions are reusable blocks of code.
Lesson 10: Structs group data; enums list named values.
Lesson 11: Arrays hold lists; slices view parts.
Lesson 12: Strings are slices of bytes.
Lesson 13: Compile and run with zig run.
Lesson 14: Common mistakes include missing semicolons.
Lesson 15: Best practices: use const, clear names, test often.
Congratulations! You have finished Module One of the Certified Zig Programming Expert course. You learned what Zig is and why it is special. You learned how to install Zig and write your first program. You learned about variables, constants, and data types. You learned control flow with if, switch, while, and for. You learned about functions, structs, enums, arrays, slices, and strings. You learned how to compile and run Zig programs. You learned common mistakes and best practices. Most importantly, you now have a solid foundation in Zig. In the next module, you will learn about memory management and error handling. Keep practising, and you will become a Zig expert!
zig run file.zig.fn with parameters and return type.try and catch (in later modules).Match the term to its meaning.
| Term | Meaning |
|---|---|
| 1. Zig | A. A group of related data |
| 2. Variable | B. A modern programming language |
| 3. Function | C. A box that can change |
| 4. Struct | D. A reusable block of code |
| 5. Enum | E. A list of named values |
Answers: 1-B, 2-C, 3-D, 4-A, 5-E
const name = "Ada";var score: u8 = 70;if statement.for loop with a function.enum.Title: “Write a Simple Calculator Together”
Instructions: In groups of 3–4, write a Zig program that adds, subtracts, multiplies, and divides two numbers. One person writes the code, one person tests it, one person fixes errors, and one person presents. Share your calculator with the class.
Goal: Practice using functions, variables, and control flow.
Task: Write a Zig program that:
if statement to check if your age is above 12.Hint: Use std.debug.print with {s} and {d}.
Project: “My First Zig Program”
Write a Zig program that does the following:
Example output:
Hello, my name is Ada. I am 13 years old. I am a teenager. Count: 1 2 3 4 5 Sum: 7 Person: Ada, age 13
Assignment: Write a Zig program that acts as a simple quiz. The program should:
if statements to check answers.Submit: Your .zig file and a screenshot of the output.
var and const for variables and constants.zig run.In Module Two, we will learn about memory management and error handling in Zig. We will cover:
To prepare, make sure you have completed the practical assignment and have your Zig environment ready. Review the key vocabulary. Think about how memory works in a computer. Bring your curiosity!
See you in Module Two!
End of Module One – Certified Zig Programming Expert
“Certified Zig Programming Expert” – Master the language of modern systems programming
Welcome back, young Zig programmer! In Module One, you learned what Zig is, how to install it, and how to write your first programs. You learned about variables, constants, data types, control flow, functions, structs, enums, arrays, slices, and strings. You have built a strong foundation.
Now it is time to learn something very important: memory management and error handling. Every computer has memory, which is like a giant warehouse where programs store their data. In some languages, the computer manages memory for you. In Zig, you are the boss of your memory. You decide when to use it and when to free it.
This might sound scary, but it is actually very powerful. It makes your programs fast and safe. Zig also has a wonderful way of handling errors using things called optionals and error unions. These help you write programs that do not crash.
In this module, we will learn about pointers, allocators, defer, errdefer, optionals, error unions, try, catch, comptime, and generics. By the end, you will write memory-safe Zig programs with proper error handling. Let's begin!
After finishing this module, you will be able to:
defer and errdefer to clean up resources.try and catch for error handling.comptime.Ngozi is 14 years old and lives in Enugu. She has been learning Zig with her older brother. One day, her brother said, “Ngozi, let me teach you about memory. Imagine your computer has a giant warehouse. Every time you create a variable, you take a box from the warehouse and put something inside.”
Ngozi understood. “So when I write var age: u8 = 13;, I am taking a small box and putting the number 13 in it?” she asked. “Exactly!” her brother said.
“But what happens if I take too many boxes and never return them?” Ngozi asked. “That is called a memory leak,” her brother said. “The warehouse runs out of boxes, and the computer slows down. In Zig, you must return the boxes when you are done.”
Ngozi learned about allocators, which are like warehouse managers. She learned about defer, which is a way of saying, “When I finish this task, please return the box.” She learned about try and catch, which help her handle problems gracefully.
By the end of the day, Ngozi had written a small program that used memory carefully. Nothing leaked, and nothing crashed. Her brother said, “You are becoming a real Zig programmer!”
Moral of the story: In Zig, you are the boss of memory. You decide when to take it and when to give it back. Zig's tools (allocators, defer, try, catch) help you do this safely.
Definition: Memory is the part of a computer where programs store data while they are running.
Why it is important: Without memory, programs cannot store or use data.
Simple explanation: Imagine a giant warehouse with many boxes. Each box holds a piece of data.
Real-life example: A bank stores customer balances in memory.
School example: A school stores student scores in memory.
Home example: A family stores a shopping list in memory.
Nigerian example: A trader stores the day’s sales in memory.
Illustration:
Memory = Giant Warehouse
+------+ +------+ +------+ +------+
| Box1 | | Box2 | | Box3 | | Box4 |
+------+ +------+ +------+ +------+
| | | |
age=13 name="Ada" score=90 price=500
Mini summary: Memory is where programs store data. It is like a warehouse full of boxes.
Definition: A pointer is a variable that holds the address of another variable in memory.
Why it is important: Pointers let you access and change data in memory directly.
Simple explanation: Imagine a box with a note inside that says, “Look in Box 5.” That is a pointer.
Real-life example: A bank uses pointers to find customer accounts quickly.
School example: A teacher uses pointers to find student records.
Home example: A family uses pointers to find items in storage.
Nigerian example: A trader uses pointers to find products in a warehouse.
Illustration:
Pointer Example:
var age: u8 = 13;
const ptr = &age; // ptr points to age
std.debug.print("{d}\n", .{ptr.*}); // prints 13
& = "address of"
.* = "value at"
Step-by-step:
& to get its address..* to get the value through the pointer.Mini summary: A pointer holds an address. Use & to get addresses and .* to get values.
Definition: An allocator is a tool that gives your program memory when it needs it.
Why it is important: Allocators manage memory carefully so your program does not waste it.
Simple explanation: Like a librarian who gives you books and takes them back when you are done.
Real-life example: Banks use allocators to handle many transactions.
School example: Schools use allocators for student data.
Home example: Families use allocators for shopping lists that grow.
Nigerian example: Businesses use allocators for growing sales data.
Illustration:
Allocator = Memory Manager
Program → Allocator → Memory
|
V
"Give me 100 bytes"
|
V
"Here you go" → Use → "Free it"
Mini summary: Allocators provide and manage memory. They are essential for growing data.
Definition: GeneralPurposeAllocator is Zig's general-purpose memory manager. It is safe and detects memory leaks.
Why it is important: It is the recommended allocator for most programs.
Simple explanation: Like a smart warehouse manager who tracks every box you take and reminds you to return it.
Real-life example: Banks use it to track all memory usage.
School example: Schools use it to avoid leaks.
Home example: Families use it to keep memory clean.
Nigerian example: Businesses use it for safe programs.
Illustration:
Using GeneralPurposeAllocator:
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const memory = try allocator.alloc(u8, 10);
defer allocator.free(memory);
Step-by-step:
GeneralPurposeAllocator.defer to clean it up..allocator().alloc to get memory.free to release it (often with defer).Mini summary: GeneralPurposeAllocator is the recommended allocator. It tracks memory and detects leaks.
Definition: ArenaAllocator is an allocator that frees all memory at once.
Why it is important: It is fast and simple for short-lived tasks.
Simple explanation: Like using a big box, then throwing the whole box away when done.
Real-life example: Banks use it for one-time tasks.
School example: Schools use it for one-time reports.
Home example: Families use it for one-time lists.
Nigerian example: Traders use it for daily summaries.
Illustration:
Using ArenaAllocator: var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); _ = try allocator.alloc(u8, 100); _ = try allocator.alloc(u8, 200); // All freed when arena.deinit() runs
Step-by-step:
ArenaAllocator.defer to free everything at the end.Mini summary: ArenaAllocator frees all memory at once. It is fast and simple.
Definition: defer runs code when a block ends. errdefer runs only when an error occurs.
Why it is important: They ensure cleanup always happens.
Simple explanation: Like setting an alarm to remind you to lock the door when you leave.
Real-life example: Banks use defer to close files.
School example: Schools use defer to close books.
Home example: Families use defer to turn off lights.
Nigerian example: Businesses use defer to release resources.
Illustration:
defer:
const file = try openFile("data.txt");
defer file.close(); // runs at end of block
errdefer:
const memory = try allocator.alloc(u8, 10);
errdefer allocator.free(memory); // runs if error
Step-by-step:
defer right after you take a resource.errdefer when you only want cleanup on errors.Mini summary: defer runs at block end. errdefer runs only on error. They ensure cleanup.
Definition: An optional is a value that might or might not exist. It is written as ?T.
Why it is important: Optionals let you handle missing values safely.
Simple explanation: Like a box that might be empty or might have something inside.
Real-life example: A bank looks up an account that might not exist.
School example: A teacher looks up a student who might not be enrolled.
Home example: A family looks up a phone number that might be missing.
Nigerian example: A trader looks up a price that might not be set.
Illustration:
Optional Example:
var maybe_age: ?u8 = null;
maybe_age = 13;
if (maybe_age) |age| {
std.debug.print("Age: {d}\n", .{age});
} else {
std.debug.print("No age given.\n", .{});
}
Step-by-step:
?T to declare an optional.null for empty.if (maybe) |value| to get the value.else for the missing case.Mini summary: Optionals are values that might be missing. Use ?T and handle both cases.
Definition: An error union is a value that is either a result or an error. It is written as !T.
Why it is important: Error unions let functions report problems cleanly.
Simple explanation: Like a package that might be delivered or might be lost.
Real-life example: A bank transfers money that might fail.
School example: A teacher grades a test that might have errors.
Home example: A family buys an item that might be out of stock.
Nigerian example: A trader sells an item that might be unavailable.
Illustration:
Error Union Example:
fn divide(a: i32, b: i32) !i32 {
if (b == 0) return error.DivideByZero;
return a / b;
}
const result = divide(10, 2) catch |err| {
std.debug.print("Error: {}\n", .{err});
return;
};
Step-by-step:
!T as the return type.error.Name.try or catch.Mini summary: Error unions report results or errors. Use !T and handle both cases.
Definition: try passes an error up. catch handles it here.
Why it is important: They make error handling clean and clear.
Simple explanation: Like passing a problem to your teacher (try) or solving it yourself (catch).
Real-life example: Banks use try to pass errors up.
School example: Schools use try in functions.
Home example: Families use catch to handle problems.
Nigerian example: Businesses use try in their code.
Illustration:
try:
fn doWork() !void {
const data = try readFile("data.txt");
std.debug.print("Got data: {s}\n", .{data});
}
catch:
const data = readFile("data.txt") catch |err| {
std.debug.print("Failed: {}\n", .{err});
return;
};
Step-by-step:
try to propagate errors up.catch to handle errors locally.catch |err| to get the error value.catch unreachable only if you are sure there is no error.Mini summary: try passes errors up; catch handles them. Use them to control errors.
Definition: comptime means code that runs while the program is being compiled, not while it is running.
Why it is important: comptime makes programs faster and safer.
Simple explanation: Like preparing ingredients before cooking, instead of during cooking.
Real-life example: Banks precompute interest tables.
School example: Schools precompute timetables.
Home example: Families prepare meals in advance.
Nigerian example: Traders prepare price lists in advance.
Illustration:
comptime Example:
const size = comptime 5 + 5; // computed at compile time
fn makeArray() [size]u8 {
return [_]u8{0} ** size;
}
Step-by-step:
comptime before a value or block.Mini summary: comptime runs at compile time. It makes programs faster and safer.
Definition: Generics are functions or types that work with many types.
Why it is important: Generics let you write code once and use it with many types.
Simple explanation: Like a single tool that fits many sizes.
Real-life example: Banks use generic functions for different account types.
School example: Schools use generic functions for different subjects.
Home example: Families use generic containers for different items.
Nigerian example: Traders use generic lists for different products.
Illustration:
Generic Function:
fn add(comptime T: type, a: T, b: T) T {
return a + b;
}
const x = add(i32, 3, 4); // 7
const y = add(f64, 3.5, 4.5); // 8.0
Step-by-step:
comptime T: type as a parameter.T in the rest of the function.Mini summary: Generics let you write one function for many types. Use comptime T: type.
Definition: Mistakes happen. Knowing them helps you avoid them.
Why it is important: Small mistakes can cause crashes or leaks.
Simple explanation: Like forgetting to close a door.
Real-life example: Banks avoid leaks in their software.
School example: Students learn from memory errors.
Home example: Families avoid wasting resources.
Nigerian example: Businesses fix memory bugs quickly.
Table of common mistakes:
| Mistake | What Happens | How to Fix |
|---|---|---|
Forgetting defer | Memory leak | Use defer right after allocation |
| Using freed memory | Crash | Never use after free |
| Not handling errors | Crash | Use try or catch |
Using unreachable wrongly | Crash | Use only when sure |
| Optional without check | Crash | Use if (maybe) |v| |
| Wrong allocator lifetime | Crash | Use correct scope |
Mini summary: Common mistakes: missing defer, using freed memory, ignoring errors. Fix them early.
Definition: Best practices are good habits that keep your code clean and safe.
Why it is important: Good habits prevent bugs and crashes.
Simple explanation: Like washing your hands before cooking.
Real-life example: Banks follow strict coding rules.
School example: Students follow coding guidelines.
Home example: Families keep resources organized.
Nigerian example: Businesses use consistent code style.
List of best practices:
defer immediately after allocation.GeneralPurposeAllocator for general use.ArenaAllocator for short-lived tasks.try to propagate; catch to handle.comptime for constants and sizes.Mini summary: Best practices: defer, free memory, handle errors, use optionals and error unions, test.
Let’s build a small program that uses memory safely.
Step 1: Create a GeneralPurposeAllocator.
Step 2: Use defer to free it.
Step 3: Allocate an array of numbers.
Step 4: Use defer to free the array.
Step 5: Fill the array with values.
Step 6: Print the values.
Step 7: Use an optional to check if values exist.
Step 8: Use error union in a helper function.
Illustration:
Safe Program Structure:
main()
|
V
GPA + defer deinit
|
V
alloc() + defer free
|
V
Fill data
|
V
Print data
|
V
Clean exit 🎉
Mini summary: A safe program uses allocators, defer, optionals, and error unions together.
You now have a powerful toolkit for memory and errors.
Your toolkit:
Illustration:
Your Toolkit: +----------+ +----------+ +----------+ | Pointers | | Allocs | | defer | +----------+ +----------+ +----------+ +----------+ +----------+ +----------+ | Optionals| | !T | | try/catch| +----------+ +----------+ +----------+ +----------+ +----------+ | comptime | | Generics | +----------+ +----------+
Mini summary: You now have a toolkit for memory and errors. Use it wisely.
| Word | Simple Definition |
|---|---|
| Memory | Where programs store data. |
| Pointer | A variable that holds an address. |
| Allocator | A tool that provides memory. |
| GeneralPurposeAllocator | A safe, general allocator. |
| ArenaAllocator | An allocator that frees all at once. |
| defer | Run code at block end. |
| errdefer | Run code only on error. |
| Optional | A value that might be missing (?T). |
| Error union | A result or error (!T). |
| try | Pass an error up. |
| catch | Handle an error here. |
| comptime | Code that runs at compile time. |
| Generic | Code that works with many types. |
| Memory leak | Memory that is never freed. |
| null | Empty optional value. |
& and .*.?T.!T.comptime T: type.GeneralPurposeAllocator.defer _ = gpa.deinit();.const allocator = gpa.allocator();.alloc to get memory.defer allocator.free(memory);.!T.error.Name.try to propagate errors.catch to handle them.catch |err| to get the error.?T.null for empty.if (maybe) |v| to unwrap.else for missing.comptime T: type as a parameter.T in the body.comptime before a value or block.defer to close files.defer for clean code.defer to clean up after snacks.try for risky steps.defer to turn off lights.defer runs in reverse order.comptime can compute complex values.try is a shortcut for catch |err| return err?defer cleans up at block end.errdefer cleans up on error.try passes; catch handles.comptime runs at compile time.defer.unreachable wrongly.defer right after allocation.try to propagate; catch to handle.
Memory = Warehouse
+------+ +------+ +------+ +------+
| Box1 | | Box2 | | Box3 | | Box4 |
+------+ +------+ +------+ +------+
| | | |
age=13 name="Ada" score=90 price=500
ptr → age (13) & = "address of" .* = "value at"
Program → Allocator → Memory Allocate → Use → Free
defer → runs at end of block errdefer → runs only on error
Optional: ?T = value or null Error union: !T = value or error
Module 1: Zig Basics
|
V
Module 2: Memory & Errors
|
V
Module 3: Advanced Features
|
V
Module 4: Real Projects
|
V
Zig Expert 🎉
| Feature | GeneralPurposeAllocator | ArenaAllocator |
|---|---|---|
| Frees memory | One at a time | All at once |
| Speed | Moderate | Very fast |
| Best for | Long-running code | Short-lived tasks |
| Leak detection | Yes | No |
| Feature | Optional (?T) | Error Union (!T) |
|---|---|---|
| Meaning | Value or null | Value or error |
| Use for | Missing value | Possible failure |
| Handling | if (x) |v| | try / catch |
| Feature | defer | errdefer |
|---|---|---|
| Runs | Always | Only on error |
| Best for | Cleanup | Partial cleanup |
| Order | Reverse | Reverse |
| Feature | comptime | runtime |
|---|---|---|
| When | Compile time | Run time |
| Speed | Fast | Slower |
| Best for | Constants, sizes | Dynamic data |
Lesson 1: Memory is where programs store data.
Lesson 2: Pointers hold addresses of values.
Lesson 3: Allocators provide and manage memory.
Lesson 4: GeneralPurposeAllocator is safe and detects leaks.
Lesson 5: ArenaAllocator frees all memory at once.
Lesson 6: defer cleans up; errdefer cleans up on error.
Lesson 7: Optionals (?T) handle missing values.
Lesson 8: Error unions (!T) report results or errors.
Lesson 9: try passes errors; catch handles them.
Lesson 10: comptime runs at compile time.
Lesson 11: Generics work with many types.
Lesson 12: Common mistakes: missing defer, using freed memory.
Lesson 13: Best practices: defer, free memory, handle errors.
Lesson 14: Build a safe program with allocators and errors.
Lesson 15: Your toolkit includes pointers, allocators, optionals, errors, comptime, generics.
Congratulations! You have finished Module Two of the Certified Zig Programming Expert course. You learned what memory is and how to use pointers. You learned about allocators (GeneralPurposeAllocator and ArenaAllocator). You learned about defer and errdefer for cleanup. You learned about optionals and error unions. You learned to use try and catch. You learned the basics of comptime and generics. You learned common mistakes and best practices. Most importantly, you now know how to write memory-safe Zig programs with proper error handling. In the next module, you will learn about advanced features and the standard library. Keep practising, and you will become a Zig expert!
Match the term to its meaning.
| Term | Meaning |
|---|---|
| 1. Pointer | A. Runs at block end |
| 2. Allocator | B. Value or null |
| 3. defer | C. Holds an address |
| 4. Optional | D. Provides memory |
| 5. Error union | E. Value or error |
Answers: 1-C, 2-D, 3-A, 4-B, 5-E
& operator.alloc(u8, N).defer.!T (error union).?T (optional).Title: “Write a Safe Memory Program Together”
Instructions: In groups of 3–4, write a Zig program that uses an allocator to create an array, fills it with numbers, prints them, and frees the memory. Add a function that returns an error union, and handle the error with try/catch. One person writes, one person tests, one person fixes, and one person presents. Share your program with the class.
Goal: Practice using allocators, defer, and error handling.
Task: Write a Zig program that:
GeneralPurposeAllocator.u8 values.defer to free memory.try in a helper function.Hint: Start with a small program and add features one at a time.
Project: “Safe Memory Manager”
Write a Zig program that:
GeneralPurposeAllocator.defer to deinitialize the allocator.defer to free the string.Example output:
Message: Hello, Zig! Length: 11 All done safely 🎉
Assignment: Write a Zig program that acts as a small inventory system. The program should:
ArenaAllocator.try and catch.defer.Submit: Your .zig file and a screenshot of the output.
defer cleans up at block end.errdefer cleans up on error.try passes; catch handles.comptime runs at compile time.defer useful?errdefer?comptime do, and why does it matter?In Module Three, we will learn about advanced features and the standard library. We will cover:
To prepare, make sure you have completed the practical assignment and have your Zig environment ready. Review the key vocabulary. Think about how you would build larger programs. Bring your curiosity!
See you in Module Three!
End of Module Two – Certified Zig Programming Expert
“Certified Zig Programming Expert” – Master the language of modern systems programming
Welcome back, young Zig programmer! In Module One, you learned the basics of Zig: variables, functions, structs, enums, arrays, slices, and strings. In Module Two, you learned about memory management and error handling: pointers, allocators, defer, optionals, error unions, try, catch, comptime, and generics.
Now it is time to go deeper. In Module Three, we will learn about advanced features and the standard library. This is where Zig becomes truly powerful. We will explore advanced comptime metaprogramming, the Zig Build System, unit testing, the standard library, and how to work with C code.
Think of this module as moving from a small workshop to a large factory. You will learn to build bigger, more complex programs. You will learn to organize your code, test it, and use the many tools Zig provides. By the end of this module, you will be a Zig power user.
Let's begin!
After finishing this module, you will be able to:
comptime features.@typeInfo.build.zig).std).@import to organize code.@cImport and the C ABI.Ada is 15 years old and lives in Abuja. She has been learning Zig for a few weeks and has built several small programs. Now she wants to build something bigger: a small tool that helps her father’s shop keep track of inventory.
Ada started writing code, but it quickly became messy. She had one big file with hundreds of lines. She had no tests, and she kept making the same mistakes. She also needed to use code from a C library her father already had.
Her mentor said, “Ada, it is time to learn advanced Zig. You will learn how to organize your code into modules, use the build system, write tests, and even use C libraries. This is how real programmers build big projects.”
Ada learned about the Zig Build System and created a build.zig file. She split her code into several files using @import. She wrote tests using test blocks. She used the standard library for file handling. And she learned how to call C functions using @cImport.
By the end of the week, Ada’s inventory tool was clean, tested, and ready to use. Her father was very proud. Ada had become a Zig power user.
Moral of the story: Advanced Zig features let you build bigger, cleaner, and more powerful programs. Learn the build system, testing, the standard library, and C interop to become a true expert.
Definition: comptime metaprogramming is writing code that runs at compile time and can generate or modify other code.
Why it is important: It makes programs faster and lets you write flexible code.
Simple explanation: Imagine writing a recipe that writes other recipes. That is what comptime can do.
Real-life example: Banks use comptime to generate efficient code.
School example: Schools use comptime for generating timetables.
Home example: Families use comptime for meal planning.
Nigerian example: Businesses use comptime for pricing rules.
Illustration:
comptime Metaprogramming:
fn power(comptime n: u8, x: i32) i32 {
if (n == 0) return 1;
return x * power(n - 1, x);
}
const result = comptime power(3, 2); // 8 at compile time
Step-by-step:
comptime.comptime to evaluate.Mini summary: Advanced comptime lets you generate code at compile time. It makes programs faster.
Definition: Generics let you write code that works with many types.
Why it is important: Generics let you write code once and reuse it.
Simple explanation: Like a shirt that fits any size.
Real-life example: Banks use generic functions for different account types.
School example: Schools use generic containers for different subjects.
Home example: Families use generic lists for different items.
Nigerian example: Traders use generic lists for different products.
Illustration:
Generic Function:
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
const m1 = max(i32, 3, 7); // 7
const m2 = max(f64, 3.5, 2.1); // 3.5
Generic Struct:
fn Pair(comptime T: type) type {
return struct {
first: T,
second: T,
};
}
const IntPair = Pair(i32);
Step-by-step:
comptime T: type for the type.T in the function body.struct from a function.Mini summary: Generics let you write code for many types. Use comptime T: type.
Definition: @typeInfo gives you information about a type at compile time.
Why it is important: It lets you write code that adapts to any type.
Simple explanation: Like a magnifying glass that shows you what a type is made of.
Real-life example: Banks use reflection to print reports for any data type.
School example: Schools use reflection for generic printing.
Home example: Families use reflection for flexible logs.
Nigerian example: Businesses use reflection for generic reports.
Illustration:
@typeInfo Example:
fn describe(comptime T: type) void {
const info = @typeInfo(T);
switch (info) {
.Int => std.debug.print("It's an integer!\n", .{}),
.Float => std.debug.print("It's a float!\n", .{}),
.Struct => std.debug.print("It's a struct!\n", .{}),
else => std.debug.print("Something else.\n", .{}),
}
}
describe(i32); // It's an integer!
describe(f64); // It's a float!
Step-by-step:
@typeInfo(T) to get info.Mini summary: @typeInfo gives type info at compile time. It enables powerful generic code.
Definition: The Zig Build System is a way to organize and build complex Zig projects.
Why it is important: It handles dependencies, targets, and compilation steps.
Simple explanation: Like a recipe for building your entire project.
Real-life example: Banks use build systems for their software.
School example: Schools use build systems for class projects.
Home example: Families use build systems for home automation.
Nigerian example: Businesses use build systems for their tools.
Illustration:
build.zig Example:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "myprogram",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
const run_step = b.step("run", "Run the program");
run_step.dependOn(&run_cmd.step);
}
Step-by-step:
build.zig file.zig build or zig build run.Mini summary: The build system organizes complex projects. Use build.zig and zig build.
Definition: Unit tests are small programs that check if your code works.
Why it is important: Tests catch bugs early.
Simple explanation: Like checking your homework before submitting it.
Real-life example: Banks test their software before use.
School example: Students check their answers.
Home example: Families test appliances.
Nigerian example: Businesses test products.
Illustration:
Test Example:
const std = @import("std");
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "add works" {
try std.testing.expect(add(2, 3) == 5);
try std.testing.expect(add(-1, 1) == 0);
}
Run with: zig test src/main.zig
Step-by-step:
std.test "description" { ... }.try std.testing.expect(...).zig test.Mini summary: Tests verify your code. Write them with test blocks and run with zig test.
Definition: The standard library is a collection of ready-made code that comes with Zig.
Why it is important: It saves time and gives you tested tools.
Simple explanation: Like a toolbox full of useful tools.
Real-life example: Banks use std for logging.
School example: Schools use std for file handling.
Home example: Families use std for timing tasks.
Nigerian example: Businesses use std for networking.
Illustration:
Common std Modules: std.debug → printing and debugging std.heap → memory allocators std.fs → file system std.mem → memory operations std.fmt → formatting std.math → math functions std.testing → testing tools std.Thread → threading std.net → networking
Mini summary: The standard library std provides many useful tools. Learn it to save time.
Definition: File handling means reading from and writing to files on your computer.
Why it is important: Files let you save data permanently.
Simple explanation: Like saving your work in a notebook.
Real-life example: Banks save transaction records in files.
School example: Schools save student data in files.
Home example: Families save shopping lists in files.
Nigerian example: Businesses save sales in files.
Illustration:
Reading a File:
const std = @import("std");
pub fn main() !void {
const file = try std.fs.cwd().openFile("data.txt", .{});
defer file.close();
var buffer: [1024]u8 = undefined;
const bytes = try file.readAll(&buffer);
std.debug.print("Read: {s}\n", .{buffer[0..bytes]});
}
Writing a File:
const file = try std.fs.cwd().createFile("out.txt", .{});
defer file.close();
try file.writeAll("Hello, file!\n");
Step-by-step:
defer file.close().readAll or write with writeAll.try.Mini summary: Use std.fs for files. Always close with defer.
Definition: Strings are lists of characters. Zig stores them as []const u8.
Why it is important: Strings handle text in your programs.
Simple explanation: Like writing words with letters.
Real-life example: Banks store names and addresses as strings.
School example: Schools store student names as strings.
Home example: Families store messages as strings.
Nigerian example: Businesses store product names as strings.
Illustration:
String Operations:
const std = @import("std");
const name = "Ada";
const greeting = try std.fmt.allocPrint(
allocator, "Hello, {s}!", .{name}
);
defer allocator.free(greeting);
std.debug.print("{s}\n", .{greeting});
Common std.mem functions:
- eql(a, b) → equal?
- startsWith(a, b) → starts with?
- indexOf(a, b) → find position
- split(a, b) → split by delimiter
- trim(a, b) → remove whitespace
Step-by-step:
[]const u8 for strings.std.fmt.allocPrint or std.fmt.bufPrint.std.mem for searching, splitting, trimming.defer.Mini summary: Strings are []const u8. Use std.fmt and std.mem.
Definition: Collections are containers for many values.
Why it is important: Collections help organize data.
Simple explanation: Like boxes for different kinds of items.
Real-life example: Banks use lists for transactions.
School example: Schools use maps for student grades.
Home example: Families use lists for shopping.
Nigerian example: Businesses use maps for product prices.
Illustration:
Common std Collections:
std.ArrayList(T) → growable list
std.StringHashMap(V) → key-value map
std.AutoHashMap(K, V) → generic map
std.Buffer → dynamic string (older)
Example with ArrayList:
var list = std.ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
for (list.items) |item| {
std.debug.print("{d}\n", .{item});
}
Step-by-step:
defer to deinit.Mini summary: Collections like ArrayList and HashMap organize data. Use them with allocators.
Definition: @import brings code from another file into your program.
Why it is important: It helps split large programs into small files.
Simple explanation: Like bringing a tool from another toolbox.
Real-life example: Banks split code into modules.
School example: Schools split subjects into books.
Home example: Families keep tools in different drawers.
Nigerian example: Businesses organize departments.
Illustration:
Module Example:
File: math.zig
pub fn add(a: i32, b: i32) i32 {
return a + b;
}
File: main.zig
const math = @import("math.zig");
pub fn main() void {
std.debug.print("{d}\n", .{math.add(2, 3)});
}
Step-by-step:
pub functions.@import("file.zig").Mini summary: @import splits code into files. Use pub to expose functions.
Definition: Interfacing with C means calling C functions from Zig.
Why it is important: Zig can use existing C libraries.
Simple explanation: Like speaking another language to use a friend's tools.
Real-life example: Banks use C libraries for performance.
School example: Schools use C libraries for math.
Home example: Families use C libraries for games.
Nigerian example: Businesses use C libraries for speed.
Illustration:
C Interop Example:
const c = @cImport({
@cInclude("stdio.h");
});
pub fn main() void {
_ = c.printf("Hello from C!\n");
}
Step-by-step:
@cImport to import C headers.c.functionName.build.zig if needed.Mini summary: Zig can call C code with @cImport. This lets you reuse C libraries.
Definition: Mistakes happen. Knowing them helps you avoid them.
Why it is important: Advanced code can hide subtle bugs.
Simple explanation: Like a small mistake in a big recipe.
Real-life example: Banks check their code carefully.
School example: Students check their projects.
Home example: Families double-check plans.
Nigerian example: Businesses test their tools.
Table of common mistakes:
| Mistake | What Happens | How to Fix |
|---|---|---|
| Forgetting to close files | Resource leak | Use defer file.close() |
| Not freeing strings | Memory leak | Use defer allocator.free() |
Wrong comptime usage | Compile errors | Use carefully and test |
| Bad build.zig structure | Build fails | Follow template |
| Missing test assertions | Bugs not caught | Write specific tests |
| Wrong C types | Crashes | Check C headers |
Mini summary: Common mistakes include forgetting to close files and free strings. Always test and clean up.
Definition: Best practices are good habits for clean, safe code.
Why it is important: Good habits prevent bugs and make code easy to read.
Simple explanation: Like keeping your tools sharp and organized.
Real-life example: Banks follow strict standards.
School example: Schools have writing guidelines.
Home example: Families keep the house tidy.
Nigerian example: Businesses use consistent procedures.
List of best practices:
@import.defer right after opening resources.comptime for constants and sizes.@typeInfo carefully and test.zig fmt to keep code clean.Mini summary: Best practices: modules, tests, defer, standard library, comptime, generics, zig fmt.
Let’s build a complete multi-file project.
Step 1: Create a project folder.
Step 2: Create src/ for source files.
Step 3: Create src/main.zig with the entry point.
Step 4: Create src/math.zig with helper functions.
Step 5: Create src/data.zig with structs.
Step 6: Create build.zig.
Step 7: Write tests in each file.
Step 8: Run zig build run.
Illustration:
Project Structure: myproject/ ├── build.zig ├── src/ │ ├── main.zig │ ├── math.zig │ └── data.zig └── README.md main.zig imports math.zig and data.zig. build.zig compiles everything together.
Mini summary: Multi-file projects organize code. Use @import and build.zig.
You now have a powerful toolkit for advanced Zig.
Your toolkit:
Illustration:
Your Toolkit: +----------+ +----------+ +----------+ | comptime | | Generics | |@typeInfo | +----------+ +----------+ +----------+ +----------+ +----------+ +----------+ | build.zig| | Tests | | std | +----------+ +----------+ +----------+ +----------+ +----------+ +----------+ | Files | | @import | | C interop| +----------+ +----------+ +----------+
Mini summary: Your advanced toolkit lets you build bigger, cleaner programs. Use it with confidence.
| Word | Simple Definition |
|---|---|
| comptime | Code that runs at compile time. |
| Metaprogramming | Writing code that generates code. |
| Generic | Code that works with many types. |
| @typeInfo | Built-in function for type information. |
| Build system | Tools for organizing and building projects. |
| build.zig | The build configuration file. |
| Unit test | A small program that tests a piece of code. |
| Standard library | Ready-made code that comes with Zig. |
| std | The Zig standard library. |
| ArrayList | A growable list. |
| HashMap | A key-value map. |
| @import | Bring code from another file. |
| Module | A separate file of code. |
| C interop | Using C code from Zig. |
| @cImport | Import C headers into Zig. |
build.zig.test blocks.std.@cImport.src/main.zig.build.zig.zig build.zig build run.std.test "name" { ... }.try std.testing.expect(...).zig test file.zig.std.fs.cwd().openFile(path, .{}).defer file.close().file.readAll(&buffer).std.ArrayList(T).init(allocator).defer list.deinit().try list.append(x).list.items.@cImport with @cInclude.c.functionName.build.zig if needed.comptime can compute nearly anything.ArrayList is very common.zig build handles everything?@import works like #include in C?@cImport can read C headers?comptime can loop?std?comptime generates code at compile time.@typeInfo gives type info.build.zig organizes projects.std provides many tools.@import splits code into modules.@cImport lets you use C code.comptime usage.build.zig structure.defer.defer right after opening resources.comptime for constants.@typeInfo carefully.zig fmt often.myproject/ ├── build.zig ├── src/ │ ├── main.zig │ ├── math.zig │ └── data.zig └── README.md
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
Write code
|
V
Write test
|
V
Run zig test
|
V
Fix errors
|
V
Pass ✅
openFile → defer close → read/write → use data
Module 1: Zig Basics
|
V
Module 2: Memory & Errors
|
V
Module 3: Advanced Features
|
V
Module 4: Real Projects
|
V
Zig Expert 🎉
| Feature | comptime | runtime |
|---|---|---|
| When | Compile time | Run time |
| Speed | Fast | Slower |
| Best for | Constants, generics | Dynamic data |
| Feature | ArrayList | Array |
|---|---|---|
| Size | Grows | Fixed |
| Memory | Allocator | Stack/static |
| Best for | Unknown count | Known count |
| Feature | @import | @cImport |
|---|---|---|
| Used for | Zig files | C headers |
| Language | Zig | C |
| Purpose | Modules | C interop |
| Feature | Test | Debug Print |
|---|---|---|
| Purpose | Verify code | Show data |
| Runs | On demand | Always |
| Use for | Quality | Debugging |
Lesson 1: Advanced comptime generates code at compile time.
Lesson 2: Generics work with any type.
Lesson 3: @typeInfo gives type information.
Lesson 4: build.zig organizes projects.
Lesson 5: Tests verify code with test blocks.
Lesson 6: std provides many useful tools.
Lesson 7: Files are read and written with std.fs.
Lesson 8: Strings use std.fmt and std.mem.
Lesson 9: Collections like ArrayList and HashMap store data.
Lesson 10: @import splits code into modules.
Lesson 11: C interop uses @cImport.
Lesson 12: Common mistakes: forgetting to close files, not freeing strings.
Lesson 13: Best practices: modules, tests, defer, std.
Lesson 14: Multi-file projects organize code well.
Lesson 15: Your advanced toolkit lets you build bigger programs.
Congratulations! You have finished Module Three of the Certified Zig Programming Expert course. You learned advanced comptime metaprogramming, generics, and compile-time reflection with @typeInfo. You learned about the Zig Build System and how to use build.zig. You learned to write unit tests, use the standard library, handle files, strings, and collections. You learned how to split code with @import and how to interface with C using @cImport. You learned common mistakes and best practices. Most importantly, you can now build bigger, cleaner, and more powerful Zig programs. In the next module, you will build real-world projects and earn your certification. Keep practising, and you will become a Zig expert!
test "name" { ... } and zig test.std.std.fs.cwd().openFile.std.@import to bring in other files.@cImport to import C headers.Match the term to its meaning.
| Term | Meaning |
|---|---|
| 1. comptime | A. Splits code into files |
| 2. build.zig | B. Compile-time code |
| 3. Test | C. Organizes project build |
| 4. @import | D. Verifies code |
| 5. @cImport | E. Imports C code |
Answers: 1-B, 2-C, 3-D, 4-A, 5-E
comptime T: type.build.zig and @import.test block and run zig test.std.fs.cwd().openFile.@cImport.Title: “Build a Multi-File Zig Project Together”
Instructions: In groups of 3–4, create a small multi-file Zig project with at least three files: main.zig, math.zig, and data.zig. Write tests in each file. Create a build.zig file. One person writes main.zig, one person writes math.zig, one person writes data.zig, and one person writes build.zig. Run the project with zig build run. Share your project with the class.
Goal: Practice building a multi-file Zig project with tests and a build system.
Task: Write a Zig program that:
@import to bring in a helper file.std.ArrayList to store some numbers.Hint: Start with small features and add one at a time.
Project: “My Multi-File Zig Tool”
Create a Zig project that:
build.zig file.main.zig as the entry point.math.zig module with a generic max function.data.zig module with a struct and ArrayList usage.zig build run.Example output:
Read 5 lines from input.txt Max value: 42 Wrote summary to output.txt All tests passed ✅
Assignment: Build a small command-line tool in Zig. The tool should:
build.zig to build and run.defer to close files.try and catch.Submit: Your project folder with all .zig files and a screenshot of the output.
comptime generates code at compile time.@typeInfo gives type info.build.zig organizes projects.std provides many tools.@import splits code into modules.@cImport lets you use C code.In Module Four, we will build real-world projects and prepare for certification. We will cover:
To prepare, make sure you have completed the practical assignment and have your multi-file project ready. Review the key vocabulary. Think about what kind of project you would like to build. Bring your curiosity!
See you in Module Four!
End of Module Three – Certified Zig Programming Expert
“Certified Zig Programming Expert” – Master the language of modern systems programming
Welcome to the final module, young Zig master! You have come a very long way. In Module One, you learned the basics of Zig. In Module Two, you learned memory management and error handling. In Module Three, you learned advanced features and the standard library.
Now, in Module Four, we will learn how to build real-world projects and prepare for your certification. This is where everything comes together. You will learn about concurrency (doing many things at once), networking (connecting computers), cross-compilation (building programs for other computers), performance optimization (making programs faster), and packaging your work.
Finally, you will complete your certification project. This project will show everything you have learned. By the end of this module, you will be a Certified Zig Programming Expert.
Let's begin!
After finishing this module, you will be able to:
Emeka is 15 years old and lives in Port Harcourt. He has been learning Zig for several weeks and has built many small programs. Now he wants to build something bigger: a simple chat server that lets his friends send messages to each other over the internet.
Emeka started by learning about networking. He learned that a server is a program that listens for connections from clients. He learned about sockets, which are like doors that programs use to send and receive data.
Emeka also learned about concurrency. His chat server needed to handle many clients at the same time. He learned how to use threads, which are like workers who each handle one client.
He wrote a simple TCP server in Zig. He tested it with his friends. They could all connect and send messages. Emeka was very proud.
Next, Emeka learned about cross-compilation. He wanted his server to run on his father’s Linux computer, even though he was developing on Windows. Zig made this easy with a single command.
Finally, Emeka optimized his server for speed and packaged it as a library. He shared it online and received positive feedback. Emeka had become a real Zig developer.
Moral of the story: Real-world projects combine many Zig features. Concurrency, networking, cross-compilation, and optimization make your programs powerful and portable.
Definition: Concurrency means doing many things at the same time.
Why it is important: It makes programs faster and more responsive.
Simple explanation: Imagine a chef cooking many dishes at once. That is concurrency.
Real-life example: A bank processes many transactions at once.
School example: A teacher handles many students' questions at once.
Home example: A family cooks, cleans, and watches TV at the same time.
Nigerian example: A market trader serves many customers at once.
Illustration:
Concurrency:
Without: With:
Task1 → Task2 → Task3 Task1 ─┐
Task2 ─┼→ All at once
Task3 ─┘
Mini summary: Concurrency is doing many things at the same time. It makes programs faster.
Definition: A thread is a worker inside your program that runs code independently.
Why it is important: Threads let you run many tasks at the same time.
Simple explanation: Like having many workers in a shop, each serving a different customer.
Real-life example: Banks use threads for handling many transactions.
School example: Schools use threads for many class activities.
Home example: Families use threads for many chores.
Nigerian example: Businesses use threads for handling many orders.
Illustration:
Thread Example:
const std = @import("std");
fn worker(id: u8) void {
std.debug.print("Worker {d} is running\n", .{id});
}
pub fn main() !void {
var threads: [4]std.Thread = undefined;
for (&threads, 0..) |*t, i| {
t.* = try std.Thread.spawn(.{}, worker, .{@as(u8, @intCast(i))});
}
for (&threads) |*t| t.join();
}
Step-by-step:
std.Thread.spawn to start a thread.join to wait for the thread to finish.Mini summary: Threads run tasks in parallel. Use std.Thread.spawn and join.
Definition: Thread safety means multiple threads can use shared data without breaking it. A mutex is a lock that ensures only one thread uses data at a time.
Why it is important: Without safety, threads can corrupt data.
Simple explanation: Like a single bathroom key that only one person can hold at a time.
Real-life example: Banks use locks for account balances.
School example: Schools use locks for shared resources.
Home example: Families use locks for shared appliances.
Nigerian example: Businesses use locks for shared inventory.
Illustration:
Mutex Example:
var counter: u32 = 0;
var mutex: std.Thread.Mutex = .{};
fn increment() void {
mutex.lock();
defer mutex.unlock();
counter += 1;
}
Step-by-step:
std.Thread.Mutex{}.mutex.lock() before using shared data.defer mutex.unlock().Mini summary: Mutexes protect shared data. Lock, use, unlock with defer.
Definition: Networking means connecting computers so they can send data to each other.
Why it is important: Networking enables the internet, chat, and file sharing.
Simple explanation: Like sending letters between houses in different cities.
Real-life example: Banks use networking for ATM transactions.
School example: Schools use networking for online classes.
Home example: Families use networking for Wi-Fi.
Nigerian example: Businesses use networking for online sales.
Illustration:
Networking Basics: Client → Server → Client Client: asks for data Server: provides data Network: moves data between them
Mini summary: Networking connects computers so they can share data. Clients ask; servers provide.
Definition: A socket is an endpoint for sending and receiving data over a network.
Why it is important: Sockets let your program talk to other computers.
Simple explanation: Like a phone that lets you call another phone.
Real-life example: Banks use sockets for secure transactions.
School example: Schools use sockets for online learning.
Home example: Families use sockets for smart devices.
Nigerian example: Businesses use sockets for payments.
Illustration:
Socket Basics:
Server: listens on a port
|
V
Client: connects to the server
|
V
Data flows both ways
Mini summary: Sockets are endpoints for network communication. Servers listen; clients connect.
Definition: A TCP server is a program that listens for clients and sends data reliably.
Why it is important: TCP servers power the internet.
Simple explanation: Like a shop that opens its doors for customers.
Real-life example: Banks run TCP servers for online banking.
School example: Schools run TCP servers for student portals.
Home example: Families run TCP servers for file sharing.
Nigerian example: Businesses run TCP servers for e-commerce.
Illustration:
Simple TCP Server:
const std = @import("std");
pub fn main() !void {
const address = try std.net.Address.parseIp("127.0.0.1", 8080);
var server = try address.listen(.{});
defer server.deinit();
std.debug.print("Server listening on port 8080\n", .{});
while (true) {
const conn = try server.accept();
defer conn.stream.close();
try conn.stream.writeAll("Hello from Zig!\n");
}
}
Step-by-step:
std.net.Address.parseIp.address.listen.defer server.deinit().accept to handle clients.writeAll.Mini summary: TCP servers listen for clients and send data. Use std.net to build them.
Definition: Cross-compilation means building a program for a different computer than the one you are using.
Why it is important: It lets your programs run on many systems.
Simple explanation: Like baking a cake for a friend in another city.
Real-life example: Banks build programs for Windows, Linux, and macOS.
School example: Schools build for different lab computers.
Home example: Families build for different devices.
Nigerian example: Businesses build for different customer systems.
Illustration:
Cross-Compilation:
Your Computer (Windows)
|
V
Zig Compiler
|
+--> Linux executable
|
+--> macOS executable
|
+--> Windows executable
Step-by-step:
zig build-exe -target x86_64-linux.Mini summary: Cross-compilation builds programs for other systems. Use -target with Zig.
Definition: Zig CC is a C compiler built into Zig. It can compile C code.
Why it is important: It replaces other C compilers and supports cross-compilation.
Simple explanation: Like a universal translator for C code.
Real-life example: Banks compile C libraries with Zig CC.
School example: Schools teach C with Zig CC.
Home example: Families build small C tools.
Nigerian example: Businesses compile C code easily.
Illustration:
Zig CC: zig cc -o myprogram myprogram.c This compiles C code using Zig's toolchain. Cross-compile: zig cc -target x86_64-linux -o myprogram myprogram.c
Step-by-step:
zig cc instead of gcc or clang.-target for cross-compilation.Mini summary: Zig CC compiles C code. It supports cross-compilation easily.
Definition: Performance optimization means making your program run faster and use less memory.
Why it is important: Faster programs are better for users.
Simple explanation: Like finding a faster route to school.
Real-life example: Banks optimize for millions of transactions.
School example: Schools optimize for many students.
Home example: Families optimize routines.
Nigerian example: Businesses optimize for many customers.
Illustration:
Optimization Tips: - Use ReleaseFast or ReleaseSmall - Avoid unnecessary allocations - Use comptime for constants - Choose the right data structures - Profile your code Build: zig build-exe -O ReleaseFast main.zig
Step-by-step:
Mini summary: Optimize with release modes, fewer allocations, and the right structures.
Definition: Benchmarking means measuring how fast your program runs.
Why it is important: You cannot improve what you do not measure.
Simple explanation: Like timing how long it takes to run a race.
Real-life example: Banks benchmark their systems.
School example: Schools benchmark student progress.
Home example: Families time chores.
Nigerian example: Businesses benchmark delivery times.
Illustration:
Benchmark Example:
const std = @import("std");
pub fn main() !void {
var timer = try std.time.Timer.start();
// Your code here
const elapsed = timer.read();
std.debug.print("Elapsed: {d} ns\n", .{elapsed});
}
Step-by-step:
std.time.Timer.start.timer.read().Mini summary: Benchmarking measures speed. Use std.time.Timer to time code.
Definition: Packaging means preparing your code so others can use it. Publishing means sharing it online.
Why it is important: Sharing your work helps the community.
Simple explanation: Like wrapping a gift and giving it to a friend.
Real-life example: Companies publish libraries for others.
School example: Students share class projects.
Home example: Families share recipes.
Nigerian example: Businesses share open-source tools.
Illustration:
Packaging Steps: 1. Write clean code 2. Add documentation 3. Create build.zig 4. Write tests 5. Add a README 6. Publish to GitHub or Zig package registry
Step-by-step:
build.zig.README.md.Mini summary: Package and publish your Zig code with docs, tests, and a README.
Definition: Mistakes happen. Knowing them helps you avoid them.
Why it is important: Small mistakes can cause big problems.
Simple explanation: Like forgetting to lock the door.
Real-life example: Banks avoid bugs in transactions.
School example: Students check their projects.
Home example: Families double-check plans.
Nigerian example: Businesses test their software.
Table of common mistakes:
| Mistake | What Happens | How to Fix |
|---|---|---|
| Race conditions | Data corruption | Use mutexes |
| Not closing sockets | Resource leak | Use defer |
| Wrong target | Program won't run | Check -target |
| No benchmarks | Slow program | Use std.time.Timer |
| Missing docs | Hard to use | Write a README |
| Ignoring warnings | Hidden bugs | Fix all warnings |
Mini summary: Common mistakes: race conditions, unclosed sockets, wrong targets. Test and document.
Definition: Best practices are good habits for professional code.
Why it is important: Good habits make code reliable and easy to maintain.
Simple explanation: Like keeping a clean workshop.
Real-life example: Banks follow strict standards.
School example: Students follow project guidelines.
Home example: Families keep things organized.
Nigerian example: Businesses use consistent procedures.
List of best practices:
defer.zig fmt to keep code clean.build.zig.Mini summary: Best practices: safety, tests, benchmarks, docs, formatting, community.
Your certification project brings everything together.
Project idea: Build a real-world application in Zig.
Steps:
build.zig.Illustration:
Certification Project Flow:
Idea
|
V
Plan
|
V
Code (modules, tests)
|
V
Concurrency + Networking
|
V
Cross-compile + Optimize
|
V
Package + Publish
|
V
Present 🎉
Mini summary: The certification project uses all skills. Plan carefully and build step by step.
You have learned so much. Let’s review.
Module One: Zig basics (variables, functions, structs, arrays).
Module Two: Memory and errors (pointers, allocators, optionals, error unions).
Module Three: Advanced features (comptime, generics, build system, std, C interop).
Module Four: Real projects (concurrency, networking, cross-compilation, optimization, packaging).
Next steps:
Illustration:
Certification
|
V
Portfolio
|
V
Share with others
|
V
Help others learn
|
V
Apply skills
|
V
Zig Expert 🎉
Mini summary: Your certification opens doors. Keep growing, sharing, and building.
| Word | Simple Definition |
|---|---|
| Concurrency | Doing many things at once. |
| Thread | A worker that runs code. |
| Mutex | A lock for shared data. |
| Networking | Connecting computers. |
| Socket | An endpoint for network data. |
| TCP | A reliable network protocol. |
| Server | A program that listens for clients. |
| Client | A program that connects to a server. |
| Cross-compilation | Building for another system. |
| Zig CC | A C compiler built into Zig. |
| Optimization | Making programs faster. |
| Benchmark | Measuring speed. |
| Package | Preparing code to share. |
| Publish | Sharing code online. |
| Race condition | A bug from threads sharing data. |
std.net.-target.std.time.Timer.std.Thread.spawn(.{}, function, args).join() to wait for completion.std.Thread.Mutex = .{}.mutex.lock() before use.defer mutex.unlock().std.net.Address.parseIp.address.listen.defer server.deinit().writeAll.x86_64-linux).zig build-exe -target x86_64-linux.std.time.Timer.start().timer.read().Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall?std.net works on Windows and Linux?defer.zig fmt.build.zig.
Without: With:
Task1 → Task2 → Task3 Task1 ─┐
Task2 ─┼→ All at once
Task3 ─┘
Client → Server → Client Client: connects Server: listens Data: flows both ways
Windows → Zig → Linux, macOS, Windows
Measure → Find slow part → Optimize → Measure again
Module 1: Zig Basics
|
V
Module 2: Memory & Errors
|
V
Module 3: Advanced Features
|
V
Module 4: Real Projects
|
V
Zig Expert 🎉
| Feature | Thread | Process |
|---|---|---|
| Memory | Shared | Separate |
| Speed | Fast to create | Slow to create |
| Safety | Needs locks | Isolated |
| Feature | TCP | UDP |
|---|---|---|
| Reliability | High | Low |
| Speed | Slower | Faster |
| Best for | Web, files | Streaming, games |
| Feature | Cross | Native |
|---|---|---|
| Where built | One system | Target system |
| Speed | Fast | Slow |
| Best for | Many targets | One target |
| Mode | Speed | Safety |
|---|---|---|
| Debug | Slow | High |
| ReleaseSafe | Medium | High |
| ReleaseFast | Fast | Medium |
| ReleaseSmall | Slow | Medium |
Lesson 1: Concurrency does many things at once.
Lesson 2: Threads run tasks in parallel.
Lesson 3: Mutexes protect shared data.
Lesson 4: Networking connects computers.
Lesson 5: Sockets are endpoints for network data.
Lesson 6: TCP servers listen and send reliable data.
Lesson 7: Cross-compilation builds for other systems.
Lesson 8: Zig CC compiles C code.
Lesson 9: Optimization makes programs faster.
Lesson 10: Benchmarks measure speed.
Lesson 11: Packaging shares your work.
Lesson 12: Common mistakes: race conditions, unclosed sockets.
Lesson 13: Best practices: safety, tests, benchmarks, docs.
Lesson 14: The certification project uses all skills.
Lesson 15: Your certification opens doors.
Congratulations! You have finished Module Four and the entire Certified Zig Programming Expert course. You learned about concurrency and threads. You learned to use mutexes for safety. You learned about networking and sockets. You built a simple TCP server. You learned about cross-compilation and Zig CC. You learned to optimize and benchmark code. You learned to package and publish Zig libraries. You learned common mistakes and best practices. You prepared for and completed your certification project. Most importantly, you are now a Certified Zig Programming Expert. Keep building, keep sharing, and keep learning!
std.time.Timer.build.zig and a README.Match the term to its meaning.
| Term | Meaning |
|---|---|
| 1. Thread | A. A lock for shared data |
| 2. Mutex | B. An endpoint for network data |
| 3. Socket | C. A worker that runs code |
| 4. Cross-compile | D. Measuring speed |
| 5. Benchmark | E. Building for another system |
Answers: 1-C, 2-A, 3-B, 4-E, 5-D
-target.build.zig and publish on GitHub.Title: “Build a Chat Server Together”
Instructions: In groups of 3–4, build a simple TCP chat server in Zig. Use threads to handle multiple clients. Use a mutex to protect shared data. Test with multiple terminals. One person writes the code, one person handles networking, one person handles concurrency, and one person writes tests. Share your server with the class.
Goal: Practice concurrency and networking together.
Task: Write a Zig program that:
std.time.Timer to measure total time.-target x86_64-linux.Hint: Start with a simple threaded program and add features.
Project: “My TCP Echo Server”
Create a TCP server in Zig that:
Example output:
Server listening on port 8080 Client 1 connected Received: Hello Client 2 connected Received: Hi
Assignment: Complete your certification project. Build a real-world Zig application that uses everything you have learned. Ideas include:
Requirements:
@import.build.zig.Submit: Your project folder, GitHub link, and a report on what you learned.
Congratulations! You have completed the entire Certified Zig Programming Expert course. Here are some next steps you can take:
Remember, this is just the beginning. You are now a Certified Zig Programming Expert. Keep building, keep learning, and keep growing!
End of Module Four – Certified Zig Programming Expert
🎉 Congratulations! You have completed the entire Certified Zig Programming Expert course! 🎉