Zigプログラミング言語入門:C言語の後継を目指す新星

シンプルさと制御性を追求するシステムプログラミング言語Zigの特徴、基本文法、そしてCとの相互運用性を実践的に解説します。

zig
systems
programming
c
performance

Zigプログラミング言語入門:C言語の後継を目指す新星

Zigは2016年にAndrew Kelleyが開発を始めたシステムプログラミング言語です。「C言語よりもシンプルで、より安全なシステムプログラミング」を目標に設計されており、近年その注目度が急速に高まっています。

Zigが目指すもの

Zigの設計哲学は明快です:

  • 隠れた制御フローなし:コードを読んだままに動作する
  • 隠れたメモリ確保なし:アロケータを明示的に渡す
  • プリプロセッサなし:マクロの代わりにコンパイル時コード実行
  • 未定義動作なし:すべての動作が定義されている

Rustとの違いを一言で表すなら、「Rustは安全性を強制するが、ZigはCのような制御性を保ちながらより予測可能にする」です。

インストールと最初のプログラム

# macOS
brew install zig

# バージョン確認
zig version
// hello.zig
const std = @import("std");

pub fn main() void {
    std.debug.print("こんにちは、Zig!\n", .{});
}
zig run hello.zig

基本的な型と変数

const std = @import("std");

pub fn main() void {
    // 定数(compileエラー:変更不可)
    const x: i32 = 42;

    // 変数(mutableが明示的)
    var y: f64 = 3.14;
    y += 1.0;

    // 型推論
    const z = "Hello"; // []const u8

    // 整数型: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128
    // 浮動小数: f16, f32, f64, f128
    // アーキテクチャ依存: isize, usize

    std.debug.print("x={}, y={d:.2}, z={s}\n", .{ x, y, z });
}

エラーハンドリング

Zigのエラー処理は非常に明確です。エラーユニオン型を使います。

const std = @import("std");

fn divide(a: f64, b: f64) !f64 {
    if (b == 0.0) return error.DivisionByZero;
    return a / b;
}

pub fn main() !void {
    const result = divide(10.0, 2.0) catch |err| {
        std.debug.print("エラー: {}\n", .{err});
        return;
    };
    std.debug.print("結果: {d}\n", .{result}); // 5.0

    // tryキーワードで伝播
    const result2 = try divide(10.0, 0.0);
    _ = result2;
}

!Tは「エラーまたはT型の値」を表すエラーユニオン型です。

メモリ管理:アロケータパターン

Zigにはガベージコレクタがありません。アロケータを明示的に渡すことでメモリを管理します。

const std = @import("std");

pub fn main() !void {
    // General Purpose Allocator(開発用:メモリリーク検出)
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // 動的配列(ArrayList)
    var list = std.ArrayList(i32).init(allocator);
    defer list.deinit();

    try list.append(1);
    try list.append(2);
    try list.append(3);

    for (list.items) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n", .{});
}

deferはスコープ終了時に実行される文です。リソース解放に非常に便利です。

コンパイル時コード実行(comptime)

Zigの最大の特徴の一つがcomptimeです。マクロを使わずに、通常のZigコードをコンパイル時に実行できます。

const std = @import("std");

fn makeArray(comptime T: type, comptime size: usize) [size]T {
    var arr: [size]T = undefined;
    for (&arr, 0..) |*item, i| {
        item.* = @intCast(i);
    }
    return arr;
}

pub fn main() void {
    const arr = makeArray(i32, 5);
    std.debug.print("{any}\n", .{arr}); // { 0, 1, 2, 3, 4 }
}

これにより、テンプレートメタプログラミングのような機能を、はるかに読みやすい形で実現できます。

C言語との相互運用

ZigはCとの相互運用が非常に優れています。CのコードをZigから直接呼び出せます。

const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("string.h");
});

pub fn main() void {
    _ = c.printf("CからZigへ: %s\n", "こんにちは");

    var buf: [256]u8 = undefined;
    _ = c.snprintf(&buf, buf.len, "数値: %d", 42);
    _ = c.printf("%s\n", &buf);
}

また、ZigのコードをCライブラリとしてコンパイルすることも可能です。

// add.zig
export fn add(a: i32, b: i32) i32 {
    return a + b;
}
zig build-lib add.zig -dynamic

ビルドシステム

Zigには優れた組み込みビルドシステムがあります。

// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "my-app",
        .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_step.dependOn(&run_cmd.step);
}
zig build run
zig build -Doptimize=ReleaseFast  # 最適化ビルド

クロスコンパイル

Zigのクロスコンパイル対応は業界最高レベルです。

# LinuxのARMバイナリをmacOSでビルド
zig build -Dtarget=aarch64-linux-musl

# WindowsのEXEをLinuxでビルド
zig build -Dtarget=x86_64-windows

追加のツールチェーンなしでこれができるのは驚異的です。

ZigとWASM

ZigはWASMターゲットもサポートしています。

zig build-exe src/main.zig -target wasm32-freestanding -O ReleaseSmall
// wasm向けのエクスポート関数
export fn add(a: i32, b: i32) i32 {
    return a + b;
}

Rustとの比較

特徴 Zig Rust
学習曲線 比較的緩やか 急峻
安全性強制 任意 強制
Cとの相互運用 ネイティブ FFI経由
コンパイル時間 高速 遅め
エコシステム 発展中 成熟
comptime macro(複雑)

まとめ

Zigは「C言語の精神的後継」として、シンプルさ・制御性・予測可能性を重視しています。特に:

  • C言語プロジェクトの段階的置き換え
  • 組み込みシステム開発
  • 高性能WASMモジュール
  • クロスプラットフォームツール

で威力を発揮します。まだバージョン1.0に達していませんが、Bun(JavaScriptランタイム)などの著名プロジェクトでの採用が進んでおり、今後の動向から目が離せない言語です。