Zig 言語を触ってみる
近年徐々に話題にあがりはじめている Zig という言語がある。
C 言語との ABI レベル相互運用が手軽にできるなど、低レイヤーがターゲットのプログラミング言語な気がしている。
Zig の安全性に対する評価は下記の記事にいろいろまとまっている。use-after-free や double free 、uninitialized memory などが none
という評価ということは、そこまで安全性が重視された言語ではないということ?
言語公式が推す売りポイントは
- No hidden control flow.
- No hidden memory allocations.
- No preprocessor, no macros.
メモリの管理が適切に行われているのかはAllocatorの責任としていて、標準ライブラリのGeneralPurposeDebugAllocator
ではdouble free
やリークを検出します。
個人的な感想ですが、ヒープの使用はオプションとしているのでメモリ管理に関しては言語の仕組み側で管理するような事はしないのかなと。
ありがとうございます!なるほど。Zig の設計が少しわかってきた気がします😃
Zig プロジェクトは下記のようにして開始できる。
mkdir zig-sandbox
cd zig-sandbox
zig init-exe
すると、ディレクトリ内に下記のようにファイルやディレクトリができあがる。
❯ ls --tree
.
├── build.zig
└── src
└── main.zig
VSCode 向けのエクステンションもある。
有志によるLanguage Server Protocolの実装です。
生成された Hello, world するコードは下記。
const std = @import("std");
pub fn main() anyerror!void {
std.log.info("All your codebase are belong to us.", .{});
}
ビルドして実行するには下記のコマンドを打つ。
zig build run
ようやく Hello, World できたw
const std = @import("std");
pub fn main() void {
std.debug.print("Hello, {s}!\n", .{"World"});
}
テストコードは同じファイル内に書けそうだけど、ちょっと詰まった。
書き方あってるんだろうか?Zig Learn に従って try expect を使用したら、コンパイルエラーに見舞われてテストが実行できなかった。zig-example というリポジトリがあって、そこで std.debug.assert を使っていたのでそれを使ってみている。
下記コードを参考にしてみた。
const std = @import("std");
const assert = @import("std").debug.assert;
pub fn main() void {
std.debug.print("Hello, {s}!\n", .{"World"});
}
test "while" {
var i: i32 = 2;
while (i < 100) {
i *= 2;
}
assert(i == 128);
}
リポジトリはこれ。
ちなみに型推論とか効くのかなと思って、型注釈を消してみたら次のコンパイルエラーになった。別の型として認識されている?? var
がどういう意味なのかをそもそも確かめる必要がありそう。
❯ zig test src/*.zig
./src/main.zig:9:5: error: variable of type 'comptime_int' must be const or comptime
var i = 2;
^
./src/main.zig:10:12: note: referenced here
while (i < 100) {
^
runtime variables と compile time variables という概念が存在している?あとで読んでおく。
zigの特徴としてcomptimeという概念が存在します。
名前そのままにコンパイル時に解析可能なコードかどうかということを示します。
変数宣言だけでなくコンパイル時間に計算したり、ジェネリクスの表現に使用されています。
参考:
ありがとうございます!これはおもしろいですね。読んでみます。
if 文。
test "if statement" {
const a = true;
var x: u16 = 0;
if (a) {
x += 1;
} else {
x += 2;
}
assert(x == 1);
}
for 文。
最初、print の引数に2つ以上要素を割り当てる際にどうすればいいかわからなかった。.{}
内に受け取りたい引数をカンマ区切りで入れれば解決できた。
test "for" {
const string = [_]u8{ 'a', 'b', 'c' };
for (string) |character, index| {
std.debug.print("character: {}, index: {} \n", .{ character, index });
}
}
最後に fibonacci 。条件では ||
ではなく or
を使う。
fn fibonacci(n: u16) u16 {
if (n == 0 or n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
test "fibonacci" {
const x = fibonacci(10);
assert(x == 55);
}