코드 예제

메모리 누수 감지

std.heap.GeneralPurposeAllocator를 사용하면 이중 해제 및 메모리 누수를 추적할 수 있습니다.

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);
    _ = u32_ptr; // silences unused variable error

    // oops I forgot to free!
}
$ zig build-exe leak.zig
$ ./leak
error(gpa): memory address 0x7f05187a4000 leaked: 
/home/runner/work/www.ziglang.org/www.ziglang.org/doctest-884cf22a/leak.zig:9:37: 0x22a5d4 in main (leak)
    const u32_ptr = try alloc.create(u32);
                                    ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:527:37: 0x222d4a in std.start.callMain (leak)
            const result = root.main() catch |err| {
                                    ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:469:12: 0x206dae in std.start.callMainWithArgs (leak)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:383:17: 0x205e36 in std.start.posixCallMainAndExit (leak)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^


thread 3326 panic: reached unreachable code
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/debug.zig:226:14: 0x204ebb in std.debug.assert (leak)
    if (!ok) unreachable; // assertion failure
             ^
/home/runner/work/www.ziglang.org/www.ziglang.org/doctest-884cf22a/leak.zig:5:27: 0x22a64c in main (leak)
    defer std.debug.assert(!gpalloc.deinit());
                          ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:527:37: 0x222d4a in std.start.callMain (leak)
            const result = root.main() catch |err| {
                                    ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:469:12: 0x206dae in std.start.callMainWithArgs (leak)
    return @call(.{ .modifier = .always_inline }, callMain, .{});
           ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:383:17: 0x205e36 in std.start.posixCallMainAndExit (leak)
    std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
                ^
/home/runner/work/www.ziglang.org/www.ziglang.org/zig/lib/std/start.zig:296:5: 0x205c42 in std.start._start (leak)
    @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
    ^
(process terminated by signal)

C 상호운용성

C 헤더를 가져와 libc 및 raylib 양쪽으로 link하는 예제.

// 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는 코딩 인터뷰에 최적화 되어 있습니다 (실제로는 아님).

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 타입

Zig에서 타입은 comptime 값이며 generic 알고리즘과 자료구조를 구현하기 위해 타입을 리턴하는 함수를 사용합니다. 이 예제에서는 간단한 generic 큐를 구현하고 그 동작을 테스트할 것입니다.

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 {
            const start = this.start orelse return null;
            defer this.alloc.destroy(start);
            if (start.next) |next|
                this.start = next
            else {
                this.start = null;
                this.end = null;
            }
            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);

    try std.testing.expectEqual(int_queue.dequeue(), 25);
    try std.testing.expectEqual(int_queue.dequeue(), 50);
    try std.testing.expectEqual(int_queue.dequeue(), 75);
    try std.testing.expectEqual(int_queue.dequeue(), 100);
    try std.testing.expectEqual(int_queue.dequeue(), null);

    try int_queue.enqueue(5);
    try std.testing.expectEqual(int_queue.dequeue(), 5);
    try std.testing.expectEqual(int_queue.dequeue(), null);
}
$ zig test queue.zig
Test [1/1] test "queue"... 
All 1 tests passed.

Zig에서 cURL 사용하기

// 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;
}