Zig is a systems programming language designed to be robust, fast, and easy to learn.
It offers manual memory management, low-level control, and is an excellent choice for those looking to develop high-performance systems and software.
Installation
Install Zig on Linux
sudo apt install zig
Install Zig on Windows
- Download the latest version of Zig from the official site.
- Extract the ZIP file and add the directory to the
PATH
environment variable.
Verify the installation
To verify that Zig is installed correctly, run:
zig version
Introduction
Basic Compilation
To compile a Zig file
zig build-exe file.zig
Direct Execution
To run directly
zig run file.zig
Basic Structure of a Zig Program
const std = @import("std");
pub fn main() void {
const stdout = std.io.getStdOut().writer();
stdout.print("Hello, Zig!\n", .{}) catch {};
}
Data Types in Zig
Zig has simple data types, such as integers, floating-point numbers, and pointers, as well as composite types like structures and arrays.
Integers
i8
,i16
,i32
,i64
: Signed integers.u8
,u16
,u32
,u64
: Unsigned integers.
var a: i32 = 42;
var b: u64 = 100;
Floating-Point Numbers
f16
,f32
,f64
: Floating-point numbers.
var pi: f32 = 3.14159;
Booleans
bool
: Can betrue
orfalse
.
var is_open: bool = true;
Strings
- Strings are arrays of characters (
[]const u8
).
const greeting: []const u8 = "Hello, Zig!";
Variables and Constants
Variables
Declared with var
. Their value can change.
const pi: f32 = 3.14; // constant
var counter: i32 = 0; // variable
Constants
Declared with const
. Their value cannot change.
const pi: f32 = 3.14; // constant
var counter: i32 = 0; // variable
Operators
Arithmetic
- Addition:
+
- Subtraction:
-
- Multiplication:
*
- Division:
/
- Modulo:
%
var x = 10 + 5; // 15
var y = 10 % 3; // 1
Comparison
- Equal:
==
- Not equal:
!=
- Greater:
>
- Less:
<
- Greater or equal:
>=
- Less or equal:
<=
if x == 10 {
// code
}
Control Flow
Conditionals
If - Else
if x > 10 {
// code if the condition is true
} else {
// code if the condition is false
}
Switch
switch (x) {
1 => std.debug.print("One\n", .{}),
2 => std.debug.print("Two\n", .{}),
else => std.debug.print("Another number\n", .{}),
}
Loops
For
for (i32(i = 0); i < 10; i += 1) {
std.debug.print("i: {}\n", .{i});
}
While
var i: i32 = 0;
while (i < 10) : (i += 1) {
// code that runs while i is less than 10
}
For in collections
const arr = [_]i32{1, 2, 3};
for (arr) |value| {
// value contains the elements of the array
}
Functions
Function Definition
Zig allows defining functions with arguments and return values.
fn sum(a: i32, b: i32) i32 {
return a + b;
}
- The
pub
prefix makes the function public (visible from other Zig files). - If nothing is returned, the return type is
void
.
Function Call
const result = sum(5, 3);
std.debug.print("Result: {}\n", .{result});
Anonymous Functions (lambdas)
const multiply = fn (a: i32, b: i32) i32 {
return a * b;
};
Inline Functions
fn square(x: i32) i32 {
return x * x;
}
const result = square(5);
Generic Functions
fn swapGeneric(behavior: anytype, a: anytype, b: anytype) void {
var temp: anytype = a;
a = b;
b = temp;
}
Composite Types
Arrays
Arrays are fixed-length types.
const numbers: [5]i32 = [5]i32{1, 2, 3, 4, 5};
Slices
const slice = &numbers[1..4]; // Access elements from index 1 to 3
std.debug.print("Elements in slice: {}\n", .{slice});
Structures
Structures (structs
) are custom data types that can contain multiple values.
const Point = struct {
x: i32,
y: i32,
};
const point: Point = Point { .x = 10, .y = 20 };
Enumerations
Enumerations (enum
) define a set of possible values for a variable.
const State = enum {
Inactive,
Active,
};
const state: State = State.Active;
Unions
const Value = union {
integer: i32,
decimal: f32,
};
var v: Value = Value{ .integer = 10 };
std.debug.print("Integer value: {}\n", .{v.integer});
Memory Management
Pointers
Zig allows manual pointer management.
var x: i32 = 42;
var ptr: *i32 = &x;
Memory Allocation
const memory = std.heap.page_allocator;
const ptr = try memory.allocate(i32, 1);
ptr.* = 42;
defer memory.deallocate(ptr);
Memory Release
Zig does not have a garbage collector, so memory allocation and release must be done manually.
var allocator = std.heap.page_allocator;
const buffer = try allocator.alloc(u8, 1024); // allocate 1024 bytes
defer allocator.free(buffer); // release memory
Defer
defer
executes a statement just before the function ends. Useful for releasing resources.
const std = @import("std");
pub fn main() void {
var file = try std.fs.cwd().openFile("file.txt", .{});
defer file.close(); // executed just before the function ends
}
Error Handling
Zig does not use exceptions. Instead, it employs an error handling system based on the use of try
, catch
, and error codes.
Explicit Errors
Functions can return errors that must be handled.
fn division(a: i32, b: i32) !i32 {
if (b == 0) {
return error.DivisionByZero;
}
return a / b;
}
Try - Catch
try
: Executes an operation that may fail. If it fails, it returns the error to the caller.catch
: Catches errors.
const file = try std.fs.cwd().openFile("file.txt", .{}).catch |err| {
std.debug.print("Failed to open file: {}\n", .{err});
};
Asserts
const a: i32 = 5;
const b: i32 = 10;
assert(a < b, "a must be less than b");
Concurrency
Zig has support for asynchronous tasks using async
and await
.
Defining Asynchronous Functions
fn get_data_async() async i32 {
return 42;
}
pub fn main() void {
const data = await get_data_async();
}
Modules
Creating Modules
Create a file named my_module.zig
.
pub fn moduleFunction() void {
std.debug.print("Module function called\n", .{});
}
Importing Modules
const std = @import("std");
const my_module = @import("my_module.zig");
pub fn main() void {
my_module.moduleFunction();
}
Interoperability with C
Zig allows importing and using C code directly.
Importing C
const c = @cImport({
@cInclude("stdio.h");
});
pub fn main() void {
c.printf("Hello from C!\n");
}
Zig can also compile C files along with Zig. This is useful for integrating C libraries into Zig projects.