Code Examples
Memory leak detection
Using std.GeneralPurposeAllocator
you can track double frees and memory leaks.
leak.zig
const std = @import("std");
pub fn main() !void {
var gpalloc = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(!gpalloc.deinit());
const alloc = &gpalloc.allocator;
const u32_ptr = try alloc.create(u32);
// oops I forgot to free!
}
$ zig build-exe leak.zig
$ ./leak
error(std): Memory leak detected:
/home/runner/work/www.ziglang.org/www.ziglang.org/doctest-94b85c01/leak.zig:9:37: 0x22dd04 in main (leak)
const u32_ptr = try alloc.create(u32);
^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:345:37: 0x2056e4 in std.start.posixCallMainAndExit (leak)
const result = root.main() catch |err| {
^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:163:5: 0x205582 in std.start._start (leak)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
thread 5579 panic: reached unreachable code
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/debug.zig:223:14: 0x2048ab in std.debug.assert (leak)
if (!ok) unreachable; // assertion failure
^
/home/runner/work/www.ziglang.org/www.ziglang.org/doctest-94b85c01/leak.zig:5:27: 0x22dd7c in main (leak)
defer std.debug.assert(!gpalloc.deinit());
^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:345:37: 0x2056e4 in std.start.posixCallMainAndExit (leak)
const result = root.main() catch |err| {
^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:163:5: 0x205582 in std.start._start (leak)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
(process terminated by signal)
C interoperability
Example of importing a C header file and linking to both libc and raylib.
// build with `zig build-exe cimport.zig -lc -lraylib`
const ray = @cImport({
@cInclude("raylib.h");
});
pub fn main() void {
const screenWidth = 800;
const screenHeight = 450;
ray.InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
defer ray.CloseWindow();
ray.SetTargetFPS(60);
while(!ray.WindowShouldClose()) {
ray.BeginDrawing();
defer ray.EndDrawing();
ray.ClearBackground(ray.RAYWHITE);
ray.DrawText("Hello, World!", 190, 200, 20, ray.LIGHTGRAY);
}
}
Zigg Zagg
Zig is optimized for coding interviews (not really).
zag.zig
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var i: usize = 1;
while (i <= 16) : (i += 1) {
if (i % 15 == 0) {
try stdout.writeAll("ZiggZagg\n");
} else if (i % 3 == 0) {
try stdout.writeAll("Zigg\n");
} else if (i % 5 == 0) {
try stdout.writeAll("Zagg\n");
} else {
try stdout.print("{d}\n", .{i});
}
}
}
$ zig build-exe zag.zig
$ ./zag
1
2
Zigg
4
Zagg
Zigg
7
8
Zigg
Zagg
11
Zigg
13
14
ZiggZagg
16
Generic Types
In Zig types are comptime values and we use functions that return a type to implement generic algorithms and data structures. In this example we implement a simple generic queue and test its behaviour.
queue.zig
const std = @import("std");
pub fn Queue(comptime Child: type) type {
return struct {
const This = @This();
const Node = struct {
data: Child,
next: ?*Node,
};
alloc: *std.mem.Allocator,
start: ?*Node,
end: ?*Node,
pub fn init(alloc: *std.mem.Allocator) This {
return This{
.alloc = alloc,
.start = null,
.end = null,
};
}
pub fn enqueue(this: *This, value: Child) !void {
const node = try this.alloc.create(Node);
node.* = .{ .data = value, .next = null };
if (this.end) |end| end.next = node //
else this.start = node;
this.end = node;
}
pub fn dequeue(this: *This) ?Child {
if (this.start == null) return null;
const start = this.start.?;
defer this.alloc.destroy(start);
this.start = start.next;
return start.data;
}
};
}
test "queue" {
const alloc = std.testing.allocator;
var int_queue = Queue(i32).init(alloc);
try int_queue.enqueue(25);
try int_queue.enqueue(50);
try int_queue.enqueue(75);
try int_queue.enqueue(100);
std.testing.expectEqual(int_queue.dequeue(), 25);
std.testing.expectEqual(int_queue.dequeue(), 50);
std.testing.expectEqual(int_queue.dequeue(), 75);
std.testing.expectEqual(int_queue.dequeue(), 100);
std.testing.expectEqual(int_queue.dequeue(), null);
}
$ zig test queue.zig
1/1 test "queue"... OK
All 1 tests passed.
Using cURL from Zig
// compile with `zig build-exe zig-curl-test.zig --library curl --library c $(pkg-config --cflags libcurl)`
const std = @import("std");
const cURL = @cImport({
@cInclude("curl/curl.h");
});
pub fn main() !void {
var arena_state = std.heap.ArenaAllocator.init(std.heap.c_allocator);
defer arena_state.deinit();
var allocator = &arena_state.allocator;
// global curl init, or fail
if (cURL.curl_global_init(cURL.CURL_GLOBAL_ALL) != .CURLE_OK)
return error.CURLGlobalInitFailed;
defer cURL.curl_global_cleanup();
// curl easy handle init, or fail
const handle = cURL.curl_easy_init() orelse return error.CURLHandleInitFailed;
defer cURL.curl_easy_cleanup(handle);
var response_buffer = std.ArrayList(u8).init(allocator);
// superfluous when using an arena allocator, but
// important if the allocator implementation changes
defer response_buffer.deinit();
// setup curl options
if (cURL.curl_easy_setopt(handle, .CURLOPT_URL, "https://ziglang.org") != .CURLE_OK)
return error.CouldNotSetURL;
// set write function callbacks
if (cURL.curl_easy_setopt(handle, .CURLOPT_WRITEFUNCTION, writeToArrayListCallback) != .CURLE_OK)
return error.CouldNotSetWriteCallback;
if (cURL.curl_easy_setopt(handle, .CURLOPT_WRITEDATA, &response_buffer) != .CURLE_OK)
return error.CouldNotSetWriteCallback;
// perform
if (cURL.curl_easy_perform(handle) != .CURLE_OK)
return error.FailedToPerformRequest;
std.log.info("Got response of {d} bytes", .{response_buffer.items.len});
std.debug.print("{s}\n", .{response_buffer.items});
}
fn writeToArrayListCallback(data: *c_void, size: c_uint, nmemb: c_uint, user_data: *c_void) callconv(.C) c_uint {
var buffer = @intToPtr(*std.ArrayList(u8), @ptrToInt(user_data));
var typed_data = @intToPtr([*]u8, @ptrToInt(data));
buffer.appendSlice(typed_data[0 .. nmemb * size]) catch return 0;
return nmemb * size;
}