前回の続きです。Zigの私が良いなーって思ったところを書いておきます。

ArenaAllocatorとGeneralPurposeAllocatorで一気にメモリ管理の恐怖感が減る

zigには「スマートポインタ」のような仕組みが言語的にない代わりに、アロケータで管理します。 「GeneralPurposeAllocator」と「ArenaAllocator」ってのが中々良くて、 これを使うと監視下に入ったアロケーションの領域解放がfree()ないしdestroy()deinit()の1回で済むようになります。しかもリークしてれば「駄目よ」って言ってくれます。素晴らしいですね。

以下は適当な2つの配列を動的確保した謎の構造体なのですが、該当のポインタをfreeしていないことに注目してください。

const Gacha = struct {
    allocator: std.heap.ArenaAllocator,

    users: []i32,
    items: []i32,

    fn init(allocator: std.mem.Allocator) !Gacha {
        var arena = std.heap.ArenaAllocator.init(allocator);
        const alloc = arena.allocator();

        const users = try alloc.alloc(i32, 3);
        @memcpy(users[0..], &[_]i32{ 1, 2, 3 });
        const items = try alloc.alloc(i32, 3);
        @memcpy(items[0..], &[_]i32{ 1, 2, 3 });

        return Gacha{ .allocator = arena, .users = users, .items = items };
    }
    fn deinit(self: @This()) void {
        self.allocator.deinit();
    }
};

test "arr" {
    const gacha = try Gacha.init(std.testing.allocator);
    defer gacha.deinit();
    std.debug.print("users: {any}, items: {any}\n", .{ gacha.users, gacha.items });
}

配列とスライス

そもそも直近の言語と同じではありますが、配列が構造体(なはず)なので固定長配列は「値型的」に返せます。 C言語と違う画期的な所ですね。

では、C言語的な「配列へのポインタ」というのは「スライス」です。GoとかPythonとかにあるアレです。 素晴らしいのでlenとか持ってます。usizeという型はありますが、引数にsize_tなんちゃらみたいなものは必要ありません。

fn getArray() [5]i32 {
    return [_]i32{ 1, 2, 3, 4, 5 };
}

fn printSlice(s: []const i32) void {
    for (s) |elem| {
        std.debug.print("{d}\n", .{elem});
    }
}

マクロの代わりにジェネリクスとリフレクション

Cに比べて強力なのがcomptimeによるジェネリクスとリフレクション、匿名構造体かと思います。 コンパイル時に確定しているのであれば、イイ感じに読めるんですね。 で、非常に面白いのがジェネリック引数、つまり「型を示すものも関数の引数の一部」になるところですかね。これは見たことがありません。

Cの欠点の1つがマクロだと思っていて、あれがテンプレートを生み、ジェネリクスに至ったので、まさに1周回ったような機能ですね。 こういうアプローチで書けるようになる、というのはまさに「Cの進化版」みたいなものを感じられる言語になっていると思います。

fn createUser(props: anytype) User {
    const T = @TypeOf(props);
    return .{
        .id = if (@hasField(T, "id")) props.id else 0,
        .name = if (@hasField(T, "name")) props.name else "unknown",
    };
}

const user = createUser(.{ .id = 1 });
std.debug.print("{d} {s}\n", .{ user.id, user.name });

Zigの良いところ

  • コンパクト・クロスプラットフォーム・高パフォーマンス・Cとの連携
    • これは文句のつけようがなく、使えば一発で実感できる筈です。「Cに近い」と自称する言語は色々ありますが、この言語は間違いありません。Rustすらこの点肉薄だろうと思えるほどです。Cとの連携やWASMにも間違いなく強みを持っており、これから楽しみです
  • コンパイル言語なのに標準ライブラリは定義まで掘れる
    • セルフホスト言語とは言え、これ案外すごくないすか。C#とかだとここまで出来ないですから
  • 組み込み用途で活躍し、教育的価値がありそう
    • 高専とかは今後zigやっても良いかも知れないですよ。Cの発展として、かなり良い言語だと思う

Zigのこれからかなってところ

  • 開発はスローペース
    • とにかく感じるのは投資です。GitHubで33kも付いてる割には、大規模な投資があるプロジェクトには視えません。すでにある程度年月が過ぎてますが、それでも形になるまで向こう3年くらいかかりそうな気がしました。Andrewさんのモチベに期待するしかありません。皆さん寄付しましょう。GAFA…シリコンバレーの大手企業よ…この言語ですよォ
    • なんだかんだ作者ファーストの言語なので、色々やりたいことをやりつつ、のびのび進化させてる感じはある
  • パッケージマネージャがない
    • 恐らく意識はされていて、高確率で作者サイドに構想はあるでしょう。まだ随分先って感じだと思います。Rustの成功はどう見てもサードパーティのエコシステムなので、是非頑張ってほしいです
  • LSP
    • zigを使いやすくするカギは、恐らくLSPとコンパイラの説明です。既にそこそこ使えますが、ちょっと物足りなさが強いので、精度が上がると良いですね
  • 標準ライブラリ
    • 私がパッと見た限りある程度熟している気がしました。配列操作やコレクション系とかも大事なのは揃ってる感がありますしね。とはいえ、文字列とかをmemで管理するのはC/C++以外の人が見たらギョエ゙ってするかも。それ系の関数は十分に見えるんですけれど、Stringとかregexとかはあっても良いのかな。

いやー、なんか謎にzig好きになってしまった。良い言語ですよ。皆さんお試しあれ。