Zig
概要
[Zigは、2016
クイックツアー
[Zigはメモリ
基本 構文 - Zigプログラムは
main
関数 から始 まります。ここでは、stdout.writeAll()
関数 を使 って標準 出力 に文字 列 を出力 しています。- hello.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { try stdout.writeAll("Hello, World!\n"); }
実行 結果 Hello, world!
- データ
型 - Zigには
整数 型 、浮動 小数点 型 、真偽 値 などの基本 的 なデータ型 があります。- types.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const age: i32 = 25; const salary: f64 = 50000.50; const isZigFun: bool = true; const message: []const u8 = "Hello, Zig!"; const intOrNull: ?i16 = 42; const nullOrInt: ?i16 = null; const intOrError: anyerror!i16 = 178; const errorOrInt: anyerror!i16 = error.BadValue; try stdout.print("age = {}({})\n", .{age, @TypeOf(age)}); try stdout.print("salary = {}({})\n", .{salary, @TypeOf(salary)}); try stdout.print("isZigFun = {}({})\n", .{isZigFun, @TypeOf(isZigFun)}); try stdout.print("message = {s}({})\n", .{message, @TypeOf(message)}); try stdout.print("intOrNull = {?}({})\n", .{intOrNull, @TypeOf(intOrNull)}); try stdout.print("nullOrInt = {?}({})\n", .{nullOrInt, @TypeOf(nullOrInt)}); try stdout.print("intOrError = {!}({})\n", .{intOrError, @TypeOf(intOrError)}); try stdout.print("errorOrInt = {!}({})\n", .{errorOrInt, @TypeOf(errorOrInt)}); }
実行 結果 age = 25(i32) salary = 5.00005e+04(f64) isZigFun = true(bool) message = Hello, Zig!([]const u8) intOrNull = 42(?i16) nullOrInt = null(?i16) intOrError = 178(anyerror!i16) errorOrInt = error.BadValue(anyerror!i16)
制御 構造 if
、else if
、else
文 を使 って条件 分岐 ができます。while
ループやfor
ループを使 って繰返 し処理 ができます。- flow-control
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const zero: f16 = 0.0; const num = zero/zero; if (num > 0.0) { try stdout.writeAll("Positive\n"); } else if (num < 0.0) { try stdout.writeAll("Negative\n"); } else if (num == 0.0) { try stdout.writeAll("Zero\n"); } else { try stdout.print("num = {}\n", .{num}); } var a: ?u16 = 42; if (a) |*value| { try stdout.print("value.* = {}({})\n", .{value.*, @TypeOf(value.*)}); value.* = 123; } else { unreachable; } if (a) |n| { try stdout.print("n = {}({})\n", .{n, @TypeOf(n)}); } else { unreachable; } a = null; if (a) |_| { unreachable; } else { try stdout.print("a = {?}({})\n", .{a, @TypeOf(a)}); } var b: anyerror!u32 = error.BadValue; try stdout.print("b = {any}({})\n", .{b, @TypeOf(b)}); if (b) |_| { unreachable; } else |err| { try stdout.print("err = {?}({})\n", .{err, @TypeOf(err)}); } b = 4423; try stdout.print("b = {any}({})\n", .{b, @TypeOf(b)}); if (b) |n| { try stdout.print("n = {}({})\n", .{n, @TypeOf(n)}); } else |_| { unreachable; } var x: i32 = 0; while (x < 4) : (x += 1) { try stdout.print("While: Iteration {}\n", .{x}); } for (0..4) |i| { try stdout.print("For: Iteration {}\n", .{i}); } }
実行 結果 num = -nan value.* = 42(u16) n = 123(u16) a = null(?u16) b = error.BadValue(anyerror!u32) err = error.BadValue(anyerror) b = 4423(anyerror!u32) n = 4423(u32) While: Iteration 0 While: Iteration 1 While: Iteration 2 While: Iteration 3 For: Iteration 0 For: Iteration 1 For: Iteration 2 For: Iteration 3
関数 関数 はfn
キーワードを使 って宣言 します。- function.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn add(a: i32, b: i32) i32 { return a + b; } pub fn main() !void { const result = add(5, 3); try stdout.print("Sum: {}\n", .{result}); }
実行 結果 Sum: 8
構造 体 とメソッド構造 体 はstruct
キーワードを使 って定義 します。メソッドは構造 体 の関数 を定義 することで実現 します。- struct+method.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const myCar = Car.init("Toyota", 2022); try stdout.print("myCar = {}\n", .{myCar}); } const Car = struct { model: []const u8, year: u32, const Self = @This(); pub fn init(model: []const u8, year: u32) Self { return Self{ .model = model, .year = year, }; } pub fn format( car: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; try out_stream.print("Car(Model: {s}, Year: {})", .{ car.model, car.year }); } };
実行 結果 myCar = Car(Model: Toyota, Year: 2022)
ここでは、Zigの
Hello world の変遷
[Zig Language Referenceの、Hello worldの
- master@2024-04-23[4]
- 0.8.1に
同 じ - 0.12.0[5]
- 0.8.1に
同 じ - 0.11.0[6]
- 0.8.1に
同 じ - 0.10.0[7]
- 0.8.1に
同 じ - 0.9.1[8]
- 0.8.1に
同 じ - 0.8.1[9]
const std = @import("std"); pub fn main() !void { const stdout = std.io.getStdOut().writer(); try stdout.print("Hello, {s}!\n", .{"world"}); }
{}
⇒{s}
[10]- 0.7.1[11]
const std = @import("std"); pub fn main() !void { const stdout = std.io.getStdOut().writer(); try stdout.print("Hello, {}!\n", .{"world"}); }
- s/outStream/writer/
- 0.6.0[12]
const std = @import("std"); pub fn main() !void { const stdout = std.io.getStdOut().outStream(); try stdout.print("Hello, {}!\n", .{"world"}); }
初期 化 の初期 値 からtry
がなくなった。- 0.4.0[13]
- 0.2.0に
同 じ - 0.5.0[14]
const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. const stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); }
- stdout_file が
var
からconst
に変更 された。
- 0.3.0[15]
- 0.2.0に
同 じ - 0.2.0[16]
const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. var stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); }
- 0.1.1[17]
const io = @import("std").io; pub fn main() -> %void { %%io.stdout.printf("Hello, world!\n"); }
特徴
[Zigは、コンパイル
静的 型付 け:- Zigは
静的 型付 けを採用 しています。型 はコンパイル時 に解決 され、実行 時 に型 エラーを引 き起 こすことがありません。また、型 推論 もサポートされています。
- Zigは
値 型 :- Zigは、
値 型 を採用 しています。これは、オブジェクトのコピーが作成 されるため、値 型 はポインタ型 よりも安全 で、予測 可能 な動作 をします。
- Zigは、
- コンパイル
時 メタプログラミング:- Zigには、コンパイル
時 メタプログラミングをサポートするためのcomptimeブロックがあります。これにより、コンパイル時 に計算 を実行 し、コンパイル時 に情報 を収集 することができます。
- Zigには、コンパイル
- メモリ
管理 :- Zigは、メモリ
管理 について堅牢 で安全 なアプローチを採用 しています。メモリリークやダングリングポインタなどの問題 を回避 するために、コンパイル時 にメモリ安全 性 を検査 することができます。
- Zigは、メモリ
- モジュールシステム:
- Zigには、モジュールシステムがあります。モジュールは、
依存 関係 を管理 するための仕組 みを提供 します。
- Zigには、モジュールシステムがあります。モジュールは、
- ネイティブコード
生成 :- Zigは、ネイティブコードを
生成 するコンパイラを提供 します。これにより、高速 かつ効率 的 なコードを実行 することができます。
- Zigは、ネイティブコードを
- エラーハンドリング:
- Zigには、エラーハンドリング
機能 があります。エラーハンドリングは、C言語 のエラーコードや例外 処理 のようなアプローチに比 べて、より安全 で予測 可能 な動作 をすることができます。
- Zigには、エラーハンドリング
- コンパイル
型 言語 1 つまたは複数 のソースコードをコンパイルして実行 ファイルを得 、それを実行 します。静的 型付 け値 ・変数 ・関数 のパラメーター・関数 の戻 値 などの型 はコンパイル時 に検証 され、型 安全 性 が担保 されます。例外 はありません- エラー
処理 などのための例外 はありません。この用途 には、関数 の戻 値 にエラーユニオン型 やオプショナル型 を使 います。 演算 子 オーバーロードはありません演算 子 は演算 子 一覧 表 にある通 りにしか機能 しませんし、必 ずそのように機能 します。シフト演算 子 がストリーム入出力 を行 ったりはしません。関数 オーバーロードはありません関数 に与 えられたパラメーターによって違 う関数 が呼 び出 されることはありません。ただし、可変 引数 の関数 は定義 できます。その場合 も、同 じ関数 が呼 び出 されます。型 推論 が行 われます変数 宣言 時 に与 えられた初期 値 (必須 )から変数 の型 を推論 することで型 アノテーションを省略 できます。- ガベージコレクション
- ガベージコレクションはありません。
- クラスはありませんがメソッドはあります
- クラスはありませんが、コンテナー( struct, union, enum )がメソッドを
持 つことができます。- コンストラクターはありません
慣習 的 に init() と名付 けられたメソッドがコンテナーのインスタンス化 に使 われます。- デストラクターはありません
慣習 的 に deinit() と名付 けられたメソッドがコンテナーのインスタンスの解体 処理 に使 われます。deinit() はスコープでを抜 ける時 に自動的 に実行 されません。defer myObj.deinit();
のようにインスタンスの生成 時 に解体 処理 を登録 します。継承 はありません- タグ
付 きunionやusingnamespaceを使 うと継承 でしたいこと(=差分 プログラミング)が可能 ですが、構文 や仕組 みは異 なります。 - interface や protocol はありません
- Java や Go の interface や Swift の protocol はありませんが、コンパイル
時 にメソッドが定義 されているかテストし、なければエラーにするプログラミングはできます。
名前 空間 の役割 はコンテナーが担 います- namespace の
様 なキーワードはありませんが、ドットシンタックス(コンテナー.識別子 や コンテナー変数 .識別子 )でコンテナーが名前 空間 の役目 を果 たします。また、usingnamespace を使 うとドットシンタックスなしに公開 識別子 にアクセスできるようになります(ミックスインとも言 えます)。 - ジェネリックプログラミングに
対応 しています 関数 呼出 しの引数 に(コンパイル時 に既知 の)型 を渡 すことができるので、ジェネリックプログラミングを行 うことが出来 ます。- ソースファイルは
匿名 struct - コンパイル
単位 であるソースファイルは暗黙 のうちに匿名 structで、組込 み関数 @import() が返 す値 を識別子 に束縛 することで名前 空間 を構成 します。ex:const std = @import("std")
- インスタンスがスコープを
抜 けるときに実行 する処理 を登録 できます - スコープを
抜 ける理由 によって、 defer と errdefer の2種類 の処理 を登録 できます。 多 くの制御 構造 は式 と文 の構文 を持 ちます- if, while, for の3つの
制御 構文 は、値 を返 す式 構文 とブロックを持 つ文 構文 の両方 を持 ちます。switchは、値 を返 す式 構文 しかありません。 文末 の;
(セミコロン)の省略 はできません最近 の新興 言語 にして珍 しく;
は省略 できません。- ループ
構文 は else節 を持 つことができます - whileとfor の2つの
反復 構文 は、ループを「完走 」した時 に実行 する else節 を持 つことができます。 - キーワードを
識別子 にできます - PL/Iのように
無制限 にではなく@"else"
のような形式 でキーワードや数字 で始 まる識別子 を使 うことができます。 - シャドウイング
禁止 外部 スコープで使 われている識別子 と同 じ識別子 を内側 のスコープで使 うことは出来 ません。コンパイルエラーになります。関数 のパラメーターはイミュータブル関数 のパラメーターは、 const宣言 された変数 (=定数 )と同 じく書換 えることは出来 ません。
サンプルコード・ギャラリー
[都市 間 の大圏 距離
[Ruby#ユーザー
都市 間 の大圏 距離 const std = @import("std"); const stdout = std.io.getStdOut().writer(); const GeoCoord = struct { longitude: f64, latitude: f64, const Self = @This(); pub fn init(longitude: f64, latitude: f64) Self { return Self{ .longitude = longitude, .latitude = latitude, }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; var ew = "
東経 "; var ns = "北緯 "; var long = self.longitude; var lat = self.latitude; if (long < 0.0) { ew = "西経 "; long = -long; } if (lat < 0.0) { ns = "南緯 "; lat = -lat; } try out_stream.print("({s}: {}, {s}: {})", .{ ew, long, ns, lat }); } pub fn distance(self: Self, other: Self) f64 { const math = std.math; const i = math.pi / 180.0; const r = 6371.008; return math.acos(math.sin(self.latitude * i) * math.sin(other.latitude * i) + math.cos(self.latitude * i) * math.cos(other.latitude * i) * math.cos(self.longitude * i - other.longitude * i)) * r; } }; pub fn main() !void { const sites = comptime .{ .{ .name = "東京 ", .gc = GeoCoord.init(139.7673068, 35.6809591), }, .{ .name = "シドニー・オペラハウス", .gc = GeoCoord.init(151.215278, -33.856778), }, .{ .name = "グリニッジ天文台 ", .gc = GeoCoord.init(-0.0014, 51.4778), }, }; inline for (sites) |site| { try stdout.print("{s}: {}\n", .{ site.name, site.gc }); } try stdout.writeAll("---\n"); inline for (sites, 0..) |a, index| { const b = sites[(index + 1) % sites.len]; try stdout.print("{s} - {s}: {} [km]\n", .{ a.name, b.name, a.gc.distance(b.gc) }); } }実行 結果 東京 : (東経 : 1.397673068e+02,北緯 : 3.56809591e+01) シドニー・オペラハウス: (東経 : 1.51215278e+02,南緯 : 3.3856778e+01) グリニッジ天文台 : (西経 : 1.4e-03,北緯 : 5.14778e+01) ---東京 - シドニー・オペラハウス: 7.823269299386704e+03 [km] シドニー・オペラハウス - グリニッジ天文台 : 1.69872708377249e+04 [km] グリニッジ天文台 -東京 : 9.560546566490015e+03 [km]
- Zig
言語 では、特定 の名前 で定義 されたformat
メソッドは、標準 ライブラリであるstd.fmt
を拡張 し、コレクションの固有 の文字 列 化 メソッドを提供 します。Zigでは、配列 内 に文字 列 を含 む配列 は第 一 級 オブジェクトではないため、ストリーム入出力 を拡張 する方法 が安全 性 を考慮 する上 で確実 です。 新 しい機能 として、0.11.0で導入 された「multi-object for loops」があります。この新 しい構文 を使用 すると、同 じ長 さの複数 のシーケンスを反復 処理 することができます。これにより、コードがより読 みやすくなり、メンテナンスが容易 になります。- また、
for
の前 にあるinline
キーワードは、コンパイル時 にループを展開 することを意味 し、タプルの配列 であることをコンパイラに伝 えます。これにより、コンパイラがより効率 的 なコードを生成 できます。
ファイルの読出 し
[ファイルを
- ファイルの
読出 し const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const file = try std.fs.openFileAbsolute( "/etc/hosts", .{}, ); defer file.close(); var reader = std.io.bufferedReader(file.reader()); const in_stream = reader.reader(); const allocator = std.heap.page_allocator; const file_size = try file.getEndPos(); const contents = try in_stream.readAllAlloc(allocator, file_size); defer allocator.free(contents); try stdout.writeAll(contents); }
このコードは、
const std = @import("std");
:標準 ライブラリをインポートします。const stdout = std.io.getStdOut().writer();
:標準 出力 のライターを取得 します。pub fn main() !void { ... }
: プログラムのエントリーポイントとなるmain
関数 です。エラーが発生 する可能 性 があるため、!void
型 が使用 されています。const file = try std.fs.openFileAbsolute("/etc/hosts", .{});
:/etc/hosts
ファイルを絶対 パスとして開 きます。try
キーワードは、関数 がエラーを返 す可能 性 があることを示 します。defer file.close();
: ファイルの処理 が終了 した後 に、必 ずファイルを閉 じるようにします。defer
キーワードは、そのスコープが終了 する際 に処理 を実行 することを示 します。var reader = std.io.bufferedReader(file.reader());
: ファイルから読 み取 りを行 うためのBufferedReader
を作成 します。const in_stream = reader.reader();
:BufferedReader
から読 み取 りストリームを取得 します。const allocator = std.heap.page_allocator;
: ページアロケータを使用 してメモリを割 り当 てるためのアロケータを定義 します。const file_size = try file.getEndPos();
: ファイルのサイズを取得 します。const contents = try in_stream.readAllAlloc(allocator, file_size);
: ファイルの内容 を読 み取 り、アロケータを使用 してメモリを割 り当 てます。defer allocator.free(contents);
: メモリを解放 するため、contents
がスコープを抜 けた時 に必 ず実行 されるようにします。try stdout.writeAll(contents);
: ファイルの内容 を標準 出力 に書 き込 みます。エラーが発生 した場合 には、そのエラーが処理 されます。
このプログラムは、
ジェネリックな複素数 型
[ジェネリックプログラミングを
- ジェネリックな
複素数 型 const std = @import("std"); const stdout = std.io.getStdOut().writer(); const signbit = std.math.signbit; fn Complex(comptime T: type) type { return struct { real: T, imag: T, const Self = @This(); pub fn init(real: T, imag: T) Self { return Self{ .real = real, .imag = imag }; } pub fn add(self: Self, other: Self) Self { return Self{ .real = self.real + other.real, .imag = self.imag + other.imag }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { try std.fmt.formatType(self.real, fmt, options, out_stream, 1); if (signbit(self.imag)) { try out_stream.writeAll("-"); try std.fmt.formatType(-self.imag, fmt, options, out_stream, 1); } else { try out_stream.writeAll("+"); try std.fmt.formatType(self.imag, fmt, options, out_stream, 1); } return out_stream.writeAll("i"); } }; } pub fn main() !void { const F64Complex = Complex(f64); var f64cplx1 = F64Complex.init(3.0, 4.0); const f64cplx2 = F64Complex.init(1.0, -4.0); try stdout.print("{}\n", .{f64cplx1}); try stdout.print("{d:.1}\n", .{f64cplx2}); try stdout.print("{x}\n", .{f64cplx1.add(f64cplx2)}); }
実行 結果 3.0e+00+4.0e+00i 1.0-4.0i 0x1p2+0x0.0p0i
pub fn Complex(comptime T: type) type
は、要素 型 をパラメーターに複素数 型 を返 します(複素数 ではなく複素数 型 です)。- Zig には
演算 子 オーバーライドがないのでメソッドとして定義 します。- 2つの
複素数 を足 し、結果 を別 の複素数 として返 す add() - real+imag の
形式 で文字 列 化 する format()
- 2つの
ソート
[- ソートの
例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var ary = [_]i8{ 1, 4, 1, 4, 2, 1, 3, 5, 6 }; try stdout.print("befor: {any}\n", .{ary}); std.sort.insertion(i8, &ary, {}, struct { pub fn cmp(context: void, a: i8, b: i8) bool { return std.sort.asc(i8)(context, a, b); } }.cmp); try stdout.print("after: {any}\n", .{ary}); }
実行 結果 befor: { 1, 4, 1, 4, 2, 1, 3, 5, 6 } after: { 1, 1, 1, 2, 3, 4, 4, 5, 6 }
- std.sort.insertion()は、
型 ・配列 ・context・比較 関数 をパラメーターに配列 をソートします。 - Zigには、ラムダ
式 がないので匿名 structのメソッドを比較 関数 に使 いました。- この
場合 はstd.sort.insertion(i8, &ary, {}, comptime std.sort.asc(i8));
で十分 ですが - structの
配列 のあるフィールドをキーにソートしたい場合 は、匿名 structのメソッドを比較 関数 に使 う手口 が有効 ですし、ソート対象 のstructのコードに手 を入 れられるときは、structのメソッドにしても良 いでしょう(ラムダ式 ほしい)。
- この
エラトステネスの篩
[エラトステネスの
- エラトステネスの
篩 const std = @import("std"); const stdout = std.io.getStdOut().writer(); fn Eratosthenes(comptime n: u16) !void { var sieve: [n + 1]bool = undefined; for (&sieve, 0..) |*e, i| { e.* = i >= 2; } for (sieve, 0..) |_, i| { if (!sieve[i]) { continue; } try stdout.print("{} ", .{i}); var j = 2 * i; while (j <= n) : (j += i) { sieve[j] = false; } } } pub fn main() !void { try Eratosthenes(1000); }
実行 結果 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
- このコードは、0.11.0
以降 でしか動 きません。
抽象 クラス(の代替 )
[Java/
抽象 クラス(の代替 )const std = @import("std"); const stdout = std.io.getStdOut().writer(); const Point = struct { x: i32 = 0, y: i32 = 0, const Self = @This(); pub fn init(x: i32, y: i32) Self { return Self{ .x = x, .y = y }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; return out_stream.print("x:{}, y:{}", .{ self.x, self.y }); } pub fn move(self: Self, dx: i32, dy: i32) Self { self.x += dx; self.y += dy; return self; } }; const Shape = struct { location: Point = Point.init(0, 0), const Self = @This(); pub fn init(x: i32, y: i32) Self { return Self{ .location = Point.init(x, y) }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; return out_stream.print("{}", .{self.location}); } pub fn move(self: Self, dx: i32, dy: i32) Self { self.location.move(dx, dy); return self; } }; const Square = struct { shape: Shape = Shape.init(0, 0), wh: i32 = 0, const Self = @This(); pub fn init(x: i32, y: i32, wh: i32) Self { return Self{ .shape = Shape.init(x, y), .wh = wh }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; return out_stream.print("{}, wh:{}", .{ self.shape, self.wh }); } pub fn move(self: Self, dx: i32, dy: i32) Self { self.shape.move(dx, dy); return self; } pub fn area(self: Self) i32 { return self.wh * self.wh; } }; const Rectangle = struct { shape: Shape = Shape.init(0, 0), width: i32 = 0, height: i32 = 0, const Self = @This(); pub fn init(x: i32, y: i32, width: i32, height: i32) Self { return Self{ .shape = Shape.init(x, y), .width = width, .height = height }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; return out_stream.print("{}, width:{}, height:{}", .{ self.shape, self.width, self.height }); } pub fn move(self: Self, dx: i32, dy: i32) Self { self.shape.move(dx, dy); return self; } pub fn area(self: Self) i32 { return self.width * self.height; } }; const Circle = struct { shape: Shape = Shape.init(0, 0), r: i32 = 0, const Self = @This(); pub fn init(x: i32, y: i32, r: i32) Self { return Self{ .shape = Shape.init(x, y), .r = r }; } pub fn format( self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype, ) !void { _ = options; _ = fmt; return out_stream.print("{}, r:{}", .{ self.shape, self.r }); } pub fn move(self: Self, dx: i32, dy: i32) Self { self.shape.move(dx, dy); return self; } pub fn area(self: Self) f32 { return @as(f32, @floatFromInt(self.r * self.r)) * std.math.pi; } }; pub fn main() !void { const ary = comptime .{ Point.init(3, 4), Shape.init(20, 24), Rectangle.init(10, 20, 32, 24) }; inline for (ary) |x| { try stdout.print("{}({})\n", .{ @TypeOf(x), x }); } try stdout.writeAll("\n"); const shapes = comptime .{ Square.init(10, 12, 40), Rectangle.init(10, 20, 32, 24), Circle.init(10, 12, 20) }; inline for (shapes) |shape| { try stdout.print("{}({}).area() = {}\n", .{ @TypeOf(shape), shape, shape.area() }); } }
実行 結果 Point(x:3, y:4) Shape(x:20, y:24) Rectangle(x:10, y:20, width:32, height:24) Square(x:10, y:12, wh:40).area() = 1600 Rectangle(x:10, y:20, width:32, height:24).area() = 768 Circle(x:10, y:12, r:20).area() = 1.25663708e+03
- インスタンスの
文字 列 化 は、format() メソッドを定義 することで std.fmt を拡張 しています。 - area() メソッドを
持 つインスタンスを要素 とするタプルを comptime修飾 した上 で、inline な for で回 しました。- この
方法 は、コンパイル時 に全 てのインスタンスが area() メソッドを持 つことを担保 します。 - ここでは
各 インスタンスの型 が Shape を継承 してるわけではなく専 ら area() メソッドを持 つことを拠 り所 にしています。- その
意味 で、interface や protocol と似 ていますが、「共通 する特徴 の定義 」はしていません。
- その
- この
範囲 型 (の代替 )
[Zigには
範囲 型 (の代替 )const std = @import("std"); const stdout = std.io.getStdOut().writer(); const Range = struct { s: i32, e: i32, const Self = @This(); pub fn init(s: i32, e: i32) Self { return Self{ .s = s, .e = e }; } pub fn next(self: *Self) ?i32 { return if (self.s > self.e) null else blk: { const n = self.s; self.s += 1; break :blk n; }; } }; pub fn main() !void { var r = Range.init(23, 42); while (r.next()) |i| { try stdout.print("{} ", .{i}); } }
実行 結果 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
チートシート
[- コメント
// から
行末 までがコメントです。 // /* ... */ スタイルのコメントはありません。変数 宣言 -
- イミュータブル
const
識別子 :型 =初期 化 式 ;型 推論 版 const
識別子 =初期 化 式 ;
- ミュータブル
var
識別子 :型 =初期 化 式 ;
- リテラル
-
整数 リテラル123, 0b11010, 0o177, 0xbadbeef
浮動 小数点 数 リテラル3.14, 1.2e-9
文字 リテラル'a', '
漢 ', '💯'文字 列 リテラル”abc”, "
了解 🏝👅"複 数 行 に渡 る文字 列 リテラル\\
複 数 行 に渡 る場合 は \\ この様 に \\ \\ を前 置 することで \\記述 できます。
配列 リテラル[4]u16{ 2,3,5,7 }, [_]u16{1,2,3,4,5}
制御 構造 -
分岐 -
- if
-
- else
節 のないif式 if (
条件 式 )式 条件 式 が false でなければif式 の値 は式 - false ならば void
- if
式 if (
条件 式 )式 1 else式 2条件 式 が false でなければif式 の値 は式 1- false ならなば
式 2 - if
文 if (
条件 式 ) { // ブロック }条件 式 が false でなければブロックを実行 - else
節 あり if (
条件 式 ) { // ブロック1 } else { // ブロック2 }条件 式 が false でなければブロック1を実行 - else ならばブロック2を
実行 条件 式 がオプショナル型 if ( オプショナル
式 ) |変数 | { // ブロック1 } else { // ブロック2 }- オプショナル
式 が- null でなければ
変数 にキャプチャーしブロック1を実行 - null ならばブロック2を
実行
- null でなければ
条件 式 がエラーユニオン型 if ( エラーユニオン
式 ) |変数 | { // ブロック1 } else | err | { // ブロック2 }- エラーユニオン
式 が- エラーコードでなければ
変数 にキャプチャーしブロック1を実行 - エラーコードならば err にキャプチャーしブロック2を
実行
- エラーコードでなければ
- else
- switch
switch (
式 ) {値 1 =>式 1,値 2a,値 2b => |変数 2 |式 2, //変数 2にキャプチャー値 3a => | *変数 3 |式 3, //変数 3にポインターをキャプチャー elase =>式 x }
反復 -
- while
-
値 を返 すvar i = 1; const q = while (i < 10) : (i += 1) { if (i > 5) { break true; } } else false;
- ラベル
付 き var i: usize = 0; outer: while (i < 10) : (i += 1) { while (true) { continue :outer; } }
- オプショナル
型 while (オプショナル
式 ) |変数 | { // null でなければ変数 にキャプチャー // ブロック1 } else { // null だったとき // ブロック2 }- エラーユニオン
型 while (エラーユニオン
式 ) |変数 | { // エラーコードでなければ変数 にキャプチャー // ブロック1 } else |err| { // エラーコードを err にキャプチャー // ブロック2 }
- for
-
値 を返 すconst items = [_]i32 { 4, 5, 3, 4, 0 }; const q = for (items) | item | { if (item == 5) { break true; } } else false;
- ラベル
付 き outer: for ([_]i32 { 4, 5, 3, 4, 0 }) | x | { for ([_]i32 { 1, 3, 5, 7 }) | y | { if ( x == y ) { continue :outer; } } }
- コンテナー
-
- struct
-
- structリテラル
struct
型 { .メンバー1 =式 1, .メンバー2 =式 2, .メンバーn =式 n }匿名 structリテラル(タプル).{
式 1,式 2,式 n }
- enum
-
- enumリテラル
enum
型 .メンバー
- union
-
- unionリテラル
uniont
型 { .メンバー =式 }- タグ
付 きunion
関数 -
関数 定義
基礎 篇
[[TODO:
エントリーポイント
[Zigでは、
- nop.zig
pub fn main() void {}
なにもしないプログラムはこの
pub fn main() !void { // エラー
集合 型 を返 す可能 性 がある処理 }
とvoid
から !void
に
コメント
[Zigのコメントに
- 3つのコメントのタイプ: Zigでは、
通常 のコメント、ドキュメントコメント、トップレベルのドキュメントコメントの3つのタイプがサポートされています。通常 のコメントは無視 されますが、ドキュメントコメントとトップレベルのドキュメントコメントは、パッケージのドキュメントを生成 するためにコンパイラによって使用 されます。 通常 のコメント:通常 のコメントは//
で始 まり、次 の改行 文字 (LFバイト)までがコメントとして扱 われます。- ドキュメントコメント: ドキュメントコメントは、
///
で始 まります。複数 のドキュメントコメントが連続 する場合 、1つの複 数 行 のドキュメントコメントとして統合 されます。ドキュメントコメントは、それに続 く要素 を文書 化 します。 - トップレベルのドキュメントコメント: トップレベルのドキュメントコメントは、
//!
で始 まります。これは、現在 のモジュールを文書 化 します。 - ドキュメントコメントの
位置 : ドキュメントコメントは特定 の場所 にのみ許可 されています。例 えば、式 の途中 や非 ドキュメントコメントの直前 にドキュメントコメントがある場合 、コンパイルエラーになります。 通常 のコメントとの統合 :現在 、パッケージのドキュメントを生成 する際 に、通常 のコメントはドキュメントコメントと統合 されます。
Zigのコメントシステムは、コードの
通常 のコメント
[Zigでは、//
から/* … */
のスタイルの
- hello.zig
// hello.zig: const std = @import("std"); //
先頭 に @ が付 く関数 は組込 み関数 です pub fn main() !void { //次 の行 の try演算 子 がエラー集合 を返 す可能 性 があるので !void が戻 値 型 try std.io.getStdOut().writeAll("Hello, World!\n"); }- Builtin Functions
Docコメント
[Zigでは、///
から
[TODO:サンプルコードと
トップレベルDocコメント
[Zigでは、//!
から
[TODO:サンプルコードと
DocコメントおよびトップレベルDocコメントは、コンパイルzig build-exe -femit-docs ソースファイル.zig
の-femit-docs
をあたえると、 docs/
値 と型
[Zigの
値 -
整数 :整数 は、i32、i64などのサイズに応 じた型 で表 されます。算術 演算 やビット演算 などがサポートされます。浮動 小数点 数 :浮動 小数点 数 は、f32、f64などのサイズに応 じた型 で表 されます。浮動 小数点 数 演算 などがサポートされます。真偽 値 : trueとfalseの2つの値 を持 つブール型 があります。- オプション
型 :値 が存在 するかどうかを示 すオプション型 があり、nullで初期 化 されます。 - エラー
統合 : エラーコードまたは正常 な値 を保持 するエラー統合 型 があります。
- プリミティブ
型 : i32、f64、boolなどの基本 的 なデータ型 があります。さらに、ポインターサイズの整数 や特定 のABIに対応 する型 もあります。 任意 の型 : void、anyerror、typeなど、特殊 な目的 の型 があります。文字 列 リテラルとUnicodeコードポイントリテラル:文字 列 リテラルは定数 のポインターであり、UTF-8でエンコードされた文字 列 を表 します。Unicodeコードポイントリテラルは、UTF-8でエンコードされたUnicodeコードポイントを表 します。
- プリミティブ
代入 と変数 -
- const: constキーワードを
使用 して、定数 の値 を変数 に割 り当 てます。一度 割 り当 てられた値 は変更 できません。 - var: varキーワードを
使用 して、変数 を宣言 し、初期 化 して値 を変更 できるようにします。 undefined
:undefined
は変数 を初期 化 せずに残 すために使用 されます。これは、不正 な値 を意味 し、バグの兆候 です。undefined
は、任意 の型 に変換 できますが、その後 はその値 が未定義 であることを検出 することはできません。
- const: constキーワードを
Zigの
[TOD0:
さまざまな値 とその型
[- formatを
伴 うprintと値 と型 pub fn main() !void { const stdout = @import("std").io.getStdOut().writer(); try stdout.print(" (1) = {}\n", .{42}); try stdout.print(" (2) = {}\n", .{0x17}); try stdout.print(" (3) = {}\n", .{0o17}); try stdout.print(" (4) = {}\n", .{0b0100101}); try stdout.print(" (5) = {}\n", .{1e222}); try stdout.print(" (6) = {}\n", .{3.1415926536}); try stdout.print(" (7) = {}\n", .{'c'}); try stdout.print(" (8) = {c}\n", .{'c'}); try stdout.print(" (9) = {s}\n", .{"abcdef"}); try stdout.print("(10) = {}, {}\n", .{ 111, 999 }); try stdout.print("(11) = {1}, {0}\n", .{ 111, 999 }); try stdout.print("(12) = {1s}, {0}\n", .{ 111, "abc" }); try stdout.print("(13) = {0d}, {0b}, {0o}, {0x}, {0X}\n", .{ 123 }); }
実行 結果 (1) = 42 (2) = 23 (3) = 15 (4) = 37 (5) = 1.0e+222 (6) = 3.1415926536e+00 (7) = 99 (8) = c (9) = abcdef (10) = 111, 999 (11) = 999, 111 (12) = abc, 111 (13) = 123, 1111011, 173, 7b, 7B
print()
の前 の、try
は単項 演算 子 です。try
は、右 の式 のエラーユニオン式 を評価 します。もしエラーであれば、同 じエラーで現在 の関数 から戻 ます。そうでない場合 は、式 はラップされていない値 になります。- エラーユニオン
型 ( Error Union Type )を返 す関数 は、try
単項 演算 子 かcatch
二 項 演算 子 で、値 とエラーを弁別 する必要 があります(try
あるいはcatch
がないと、コンパイル時 にエラーになります)。An error occurred: /tmp/playground726918707/play.zig:3:10: error: error is ignored. consider using `try`, `catch`, or `if` stdout.print(" (1) = {}\n", .{42}); ^
- エラーユニオン
print()
の様 に、標準 ライブラリーのformat()
を使 う関数 は、書式 化 文字 列 とタプル(匿名 struct ).{ … }
を引数 にします。C言語 のような、可変 引数 ではなくタプルを使 うので[18]、プレースホルダーがない場合 でも、空 のタプル.{}
は必須 です。書式 化 文字 列 通常 の文字 列 ですが{
と}
で囲 まれたプレスホルダーが、タプルの当該 順位 の値 (を書式 化 した文字 列 )に置換 わります。書式 化 文字 列 の中 で{
あるいは}
自身 を使 いたいときには、{{
あるいは}}
と二 文字 重 ねます。- タプル
書式 化 文字 列 のプレースホルダーによって、参照 と文字 列 化 される値 のタプルです。- 2つ
以上 の値 を渡 す場合 は、第 二 引数 を .{ 1, 2, 3 } の様 にカンマ区切 りのタプルにします( {} の前 の . (点 )を忘 れがちですが、型 の省略 を意味 し必須 です)。 基本 的 に、左 から順 にプレスホルダーにタプルの値 が左 から与 えられますが、{0} {1} の書式 で参照 する引数 の順位 を明示 できます。書式 指定 と併用 する時 は、stdout.print("? = {1s}, {0}\n", .{ 111, "abc" })
の様 に順位 が先 、書式 指定 文字 が後 になります。- この
機能 は言語 (自然 言語 )によって異 なる語順 を吸収 することに使 えそうですが、fmtの第 一 引数 は comptime修飾 子 がついていて変数 にはできません。
- この
数値 (整数 と浮動 小数点 数 )や文字 リテラルと文字 列 リテラルがあり、整数 はいくつかの異 なる基数 表現 が、浮動 小数点 数 は指数 表現 と小数 表現 があります。文字 と文字 列 は明確 に異 なり、リテラルでは ’A’ が文字 (@TypeOf('A') ⇒ comptime_int)、 ”ABC” が文字 列 (@TypeOf("ABC") ⇒ *const [3:0]u8)です。嫌 な予感 がした人 の直感 は正解 です。Zig では、文字 列 は第 一 級 オブジェクトではなく文字 (u8)の配列 で、関数 から返 すときはアロケーターと defer の連携 などで記憶 域 の寿命 と値 の妥当 性 を「プログラマー」が担保 する必要 があります。また、GoのGCはありません。CrystalのASTを操作 できるマクロもありませんし、Rustの所有 権 も、C++のスマートポインターもありません。- このことは、C
言語 なみのプログラマー任 せのメモリー管理 を文字 列 以外 でも強 いられることを意味 していますが、zig(コマンド)や標準 ライブラリーのソースコードを読 むと、複数 の型 でアロケーターを使 い分 け、スタック上 のインスタンス(のハードウェア起因 のスコープ)を使 い分 けられることを実践 で証明 しているので、pre-releaseから、initial-releasまでの間 に、安定 化 ・定式 化 が図 られることを期待 します。
- このことは、C
[TODO:master/lib/std/fmt.zigを
プリミティブ型
[プリミティブ 型 ( Primitive Types )[19]型 相当 するC言語 の型 説明 i8
int8_t
符号 付 き8ビット整数 u8
uint8_t
符号 無 し8ビット整数 i16
int16_t
符号 付 き16ビット整数 u16
uint16_t
符号 無 し16ビット整数 i32
int32_t
符号 付 き32ビット整数 u32
uint32_t
符号 無 し32ビット整数 i64
int64_t
符号 付 き64ビット整数 u64
uint64_t
符号 無 し64ビット整数 i128
__int128
符号 付 き128ビット整数 u128
unsigned __int128
符号 無 し128ビット整数 isize
intptr_t
符号 付 きポインターサイズ整数 usize
uintptr_t
,size_t
符号 無 しポインターサイズ整数 c_short
short
C 言語 とのABI互換 性 のためc_ushort
unsigned short
C 言語 とのABI互換 性 のためc_int
int
C 言語 とのABI互換 性 のためc_uint
unsigned int
C 言語 とのABI互換 性 のためc_long
long
C 言語 とのABI互換 性 のためc_ulong
unsigned long
C 言語 とのABI互換 性 のためc_longlong
long long
C 言語 とのABI互換 性 のためc_ulonglong
unsigned long long
C 言語 とのABI互換 性 のためc_longdouble
long double
C 言語 とのABI互換 性 のためf16
_Float16
16ビット 浮動 小数点 数 (仮数 10ビット) IEEE-754-2008 binary16f32
float
32ビット 浮動 小数点 数 (仮数 23ビット) IEEE-754-2008 binary32f64
double
64ビット 浮動 小数点 数 (仮数 52ビット) IEEE-754-2008 binary64f80
double
80ビット 浮動 小数点 数 (仮数 64ビット) IEEE-754-2008 80ビット拡張 精度 f128
_Float128
128ビット 浮動 小数点 数 (仮数 112ビット) IEEE-754-2008 binary64bool
bool
true
またはfalse
anyopaque
void
型 消去 されたポインターvoid
( 該当 なし)0ビット 型 noreturn
( 該当 なし)break
,continue
,return
,unreachable
, andwhile (true) {}
の型 type
( 該当 なし)型 の型 anyerror
( 該当 なし)エラーコード comptime_int
( 該当 なし)コンパイル 時 に既知 の値 に対 してのみ許可 される整数 リテラルの型 。comptime_float
( 該当 なし)コンパイル 時 に既知 の値 に対 してのみ許可 される浮動 小数点 リテラルの型 。
上記 の整数 型 に加 え、任意 のビット幅 の整数 を参照 するには、識別子 としてiまたはuに続 けて数字 を用 いることができます。例 えば、識別子 i7
は符号 付 き7ビット整数 を意味 します。この表現 の整数 型 に許 される最大 ビット幅 は65535です。
プリミティブ値
[プリミティブ 型 ( Primitive Values )[20]名前 説明 true
とfalse
bool
値 null
optional 型 をnull
に設定 するために使用 されます。undefined
値 を不定 にするために使用 されます。
文字 列 リテラルとUnicodeコードポイントリテラル
[構文 (EBNF)STRINGLITERALSINGLE = "\"" string_char* "\"" skip STRINGLITERAL = STRINGLITERALSINGLE | ( line_string skip )+ ox80_oxBF = [#x80-#xBF] oxF4 = '\xF4' ox80_ox8F = [#x80-#x8F] oxF1_oxF3 = [#xF1-#xF3] oxF0 = '\xF0' ox90_0xBF = [#x90-#xBF] oxEE_oxEF = [#xEE-#xEF] oxED = '\xED' ox80_ox9F = [#x80-#x9F] oxE1_oxEC = [#xE1-#xEC] oxE0 = '\xE0' oxA0_oxBF = [#xA0-#xBF] oxC2_oxDF = [#xC2-#xDF] (* From https://lemire.me/blog/2018/05/09/how-quickly-can-you-check-that-a-string-is-valid-unicode-utf-8/ *) mb_utf8_literal = oxF4 ox80_ox8F ox80_oxBF ox80_oxBF | oxF1_oxF3 ox80_oxBF ox80_oxBF ox80_oxBF | oxF0 ox90_0xBF ox80_oxBF ox80_oxBF | oxEE_oxEF ox80_oxBF ox80_oxBF | oxED ox80_ox9F ox80_oxBF | oxE1_oxEC ox80_oxBF ox80_oxBF | oxE0 oxA0_oxBF ox80_oxBF | oxC2_oxDF ox80_oxBF ascii_char_not_nl_slash_squote = [\000-\011\013-\046-\050-\133\135-\177] char_escape = "\\x" hex hex | "\\u{" hex+ "}" | "\\" [nr\\t'"] char_char = mb_utf8_literal | char_escape | ascii_char_not_nl_slash_squote string_char = char_escape | [^\\"\n] line_string = ( "\\\\" [^\n]* [ \n]* )+
- [21]
文字 列 リテラル
[Zigにおける
Unicodeコードポイントリテラル
[Unicodeコードポイントリテラルの
エスケープシーケンス
[エスケープシーケンス( Escape Sequences )[23] エスケープシーケンス 名称 \n
Newline \r
Carriage Return \t
水平 タブ\\
バックスラッシュ 自身 \'
シングルクォーテーション \"
ダブルクォーテーション \xNN
16 進 8ビットバイト値 (2桁 )\u{NNNNNN}
16 進数 Unicode コードポイント UTF-8符号 化 (1桁 以上 )
- 註:
有効 なUnicodeポイントの最大 値 は0x10ffff
です。
マルチライン文字 列 リテラル
[マルチライン\\
トークンを\\
で
- マルチライン
文字 列 リテラル const stdout = @import("std").io.getStdOut().writer(); const message = \\
文字 列 リテラルを、エスケープ記号 がなく複 数 行 にまたがって書 くことができます。 \\複 数 行 の文字 列 リテラルを開始 するには、\\ トークンを使用 します。 \\コメントと同 じように、文字 列 リテラルは行末 まで続 きます。 \\行 の終 わりは文字 列 リテラルに含 まれません。 \\ただし、次 の行 が \\ で始 まる場合 は、改行 が追加 され文字 列 リテラルが続行 されます。 ; pub fn main() !void { try stdout.print("message = {s}", .{message}); }実行 結果 message =
文字 列 リテラルを、エスケープ記号 がなく複 数 行 にまたがって書 くことができます。複 数 行 の文字 列 リテラルを開始 するには、\\ トークンを使用 します。 コメントと同 じように、文字 列 リテラルは行末 まで続 きます。行 の終 わりは文字 列 リテラルに含 まれません。 ただし、次 の行 が \\ で始 まる場合 は、改行 が追加 され文字 列 リテラルが続行 されます
代入
[Zigにおける
const
キーワードを使用 して、識別子 に値 を割 り当 てます。const
で定義 された識別子 は変更 できません。その値 は定数 です。変数 として値 を変更 できるようにするには、var
キーワードを使用 します。変数 は初期 化 される必要 があります。初期 化 されていない変数 を使用 すると、コンパイルエラーが発生 します。初期 化 されていない変数 にはundefined
を使用 して、未定義 のままにすることができます。定数 式 や単純 型 の型 推論 にはcomptime
を使用 します。
const print = @import("std").debug.print; // constを
使 って定数 を定義 const x = 1234; pub fn main() void { //関数 内 でのconstは定数 を定義 const y = 5678; //定数 には値 を再割 り当 てできない // y = 1; // コンパイルエラー // varを使 って変数 を定義 var z: i32 = 5678; //変数 に値 を再割 り当 て z += 1; //変数 は初期 化 する必要 がある // var w: i32; comptime var w = 1; //初期 化 を伴 った宣言 は型 推論 される //変数 を undefined で初期 化 var u: i32 = undefined; u = 8; print("{d}\n", .{x}); // 1234 print("{d}\n", .{y}); // 5678 print("{d}\n", .{z}); // 5679 print("{d}\n", .{w}); // 1 print("{d}\n", .{u}); // 8 }
comptime
[comptimeは、Zig
- コンパイル
時 パラメータ (Compile-Time Parameters): Zigでは、関数 や構造 体 の型 引数 にcomptime
を使用 することができます。これにより、関数 や構造 体 が特定 の型 に対 してコンパイル時 に振 る舞 うことが保証 されます。コンパイル時 に型 が解決 されるため、特定 の型 に特 化 したコードを生成 することができます。fn example(comptime T: type) T { // Tの
特定 の型 に基 づいた処理 を行 う }
- コンパイル
時 変数 (Compile-Time Variables):comptime
修飾 子 を使用 して、コンパイル時 に評価 されることが保証 された変数 を宣言 することができます。これらの変数 は、コンパイル時 の静的 解析 に使用 され、ランタイムでのアクセスは許可 されません。const comptime MAX_VALUE: usize = 100;
- コンパイル
時 式 (Compile-Time Expressions): comptimeブロック内 で式 を記述 することで、その式 がコンパイル時 に評価 されることを保証 できます。このような式 は、コンパイル時 のみに影響 を与 える必要 がある場合 に使用 されます。例 えば、配列 のサイズや定数 の計算 に使用 されます。const arraySize = comptime 10; const comptime result = someFunction();
- コンパイル
時 制御 フロー (Compile-Time Control Flow):if
,while
,for
,switch
ステートメントなどの制御 フローは、コンパイル時 に評価 されることが保証 されます。これにより、特定 の条件 に基 づいてコンパイル時 に異 なるコードパスを取 ることができます。comptime if (someCondition) { // このブロックはコンパイル
時 に評価 される } else { // このブロックもコンパイル時 に評価 される }
comptimeの
このコードはコンパイルエラーになります。
定数 の初期 値 が関数 の戻 値 だとエラーfn mul(x: usize, y: usize) usize { return x * y; } pub fn main() void { const len : usize = mul(3, 4); const ary: [len]i32 = undefined; _ = ary; }
- コンパイル
結果 An error occurred: playground/playground3656639701/play.zig:7:17: error: unable to resolve comptime value playground/playground3656639701/play.zig:7:17: note: array length must be comptime-known
エラーメッセージの
error: unable to resolve comptime value
: コンパイル時 に解決 できない値 があります。note: array length must be comptime-known
:配列 の長 さはコンパイル時 に既知 である必要 があります。つまり、配列 の長 さは実行 時 に決定 されることは許 されません。
つまり、Zigでは
C++であれば、constexpr
が
- comptimeを
追加 しコンパイル時 に実行 fn mul(x: usize, y: usize) usize { return x * y; } pub fn main() void { const len : usize = comptime mul(3, 4); // mul の
前 に comptime を追加 const ary: [len]i32 = undefined; _ = ary; }変更 点 は mul() の呼出 しをcomptime
で修飾 しただけです。comptime
は、修飾 子 式 をコンパイル時 に実行 する修飾 子 で、式 の中 でコンパイル時 に未定 な値 が参照 されると、エラーとなります。ここでは、数 リテラル同士 の商 を求 めているので、コンパイル時 値 が確定 できます。- _ = ary は、「
未 使用 変数 」をサプレッスするときのイディオムです。
テストフレームワーク
[Zigは、
- テスト
宣言 : テストは、test
キーワードに続 いて名前 (オプション)とテストの本体 を含 むブロックで構成 されます。これにより、コードの特定 の部分 や関数 が期待 どおりに動作 するかを検証 できます。 - テスト
実行 :zig test
コマンドを使用 してテストを実行 します。このコマンドは、テスト用 のビルドを作成 し、デフォルトのテストランナーを実行 します。テストランナーは、テスト宣言 を見 つけて実行 し、その結果 を出力 します。 - テスト
結果 の報告 : テストランナーは、テスト結果 を標準 エラーに出力 します。成功 したテスト、失敗 したテスト、スキップされたテストなどの情報 が報告 されます。これにより、開発 者 はテストの状態 を把握 し、問題 がある場合 は修正 できます。 - テストのスキップ:
error.SkipZigTest
を返 すことでテストをスキップすることができます。また、zig test
コマンドに--test-filter
オプションを指定 して、特定 のテストのみを実行 することもできます。 - テストの
自動 化 : テストはコードの一部 として記述 されるため、変更 があるたびに手動 で実行 する必要 がありません。CI/CDパイプラインなどの自動 化 ツールと統合 して、コードの品質 を継続 的 に確認 できます。
Zigのテストフレームワークは、コードの
- テスト
宣言 の構文 (EBNF) test-decl = [ doc_comment ] "test" [ STRINGLITERALSINGLE ] block
- [21]
- if-test.zig
const std = @import("std"); const expectEqual = std.testing.expectEqual; test "if expr" { const f = true; var x: usize = 5; x += if (f) 10 else 20; try expectEqual(x, 15); } test "if stmt" { const f = true; var x: isize = 10; if (!f) { x += 10; } else { x -= 20; } try expectEqual(x, -10); }
- コマンドライン
% zig test if-test.zig All 2 tests passed.
このZigのテストコードは、 if
if
- "if expr":
if
式 を使用 して、条件 に応 じて異 なる値 を返 し、その結果 を変数 に代入 しています。その後 、変数 の値 が期待 通 りであることを確認 します。 - "if stmt":
if
文 を使用 して、条件 に応 じて異 なるステートメントを実行 し、その結果 を変数 に代入 しています。その後 、変数 の値 が期待 通 りであることを確認 します。
テスト
このように、Zigのテストフレームワークを
- fdiv-inf-nan.zig
const std = @import("std"); const expect = std.testing.expect; fn fdiv(n: f64, d: f64) f64 { return n / d; } const inf = std.math.inf(f64); const nan = std.math.nan(f64); test "fdiv 1" { try expect(fdiv(123.0, 111.1) == 123.0 / 111.1); } test "fdiv 2" { try expect(fdiv(123.0, 0.0) == inf); } test "fdiv 3" { try expect(fdiv(0.0, 0.0) == nan); }
- コマンドライン
% zig version 0.11.0 % zig test fdiv-inf-nan.zig Test [3/3] test.fdiv 3... FAIL (TestUnexpectedResult) /usr/local/lib/zig/std/testing.zig:515:14: 0x2248df in expect (test) if (!ok) return error.TestUnexpectedResult; ^ /usr/home/user1/tut/zig/fdiv-inf-nan.zig:20:5: 0x224b0b in test.fdiv 3 (test) try expect(fdiv(0.0, 0.0) == nan); ^ 2 passed; 0 skipped; 1 failed. error: the following test command failed with exit code 1: /home/user1/.cache/zig/o/00aa6779b54996b883ad0fc42233ea3d/test
このfdiv-inf-nan.zig
というファイルにfdiv
- "fdiv 1":
正常 な割 り算 が行 われることを確認 します。fdiv(123.0, 111.1)
が123.0 / 111.1
と等 しいかどうかを期待 します。 - "fdiv 2": 0で
割 った場合 、無限 大 を返 すことを確認 します。fdiv(123.0, 0.0)
がinf
と等 しいかどうかを期待 します。 - "fdiv 3": 0を0で
割 った場合 、非 数 を返 すことを確認 します。fdiv(0.0, 0.0)
がnan
と等 しいかどうかを期待 します。
テストをfdiv(0.0, 0.0)
が nan
と
このfdiv
- fdiv-inf-nan-FIX.zig
const std = @import("std"); const expect = std.testing.expect; fn fdiv(n: f64, d: f64) f64 { return n / d; } const inf = std.math.inf(f64); const nan = std.math.nan(f64); const isNan = std.math.isNan; test "fdiv 1" { try expect(fdiv(123.0, 111.1) == 123.0 / 111.1); } test "fdiv 2" { try expect(fdiv(123.0, 0.0) == inf); } test "fdiv 3" { try expect(isNan(fdiv(0.0, 0.0))); }
- コマンドライン
% zig test fdiv-inf-nan-FIX.zig All 3 tests passed.
fdiv-inf-nan-FIX.zig
ファイルでは、3
このfdiv(0.0, 0.0)
のnan
であるかどうかをstd.math.isNan
nan
) であるかどうかを
このfdiv
nan
) を
変数
[Zigでは、
-
変数 の宣言 :var
キーワードまたはconst
キーワードを使用 して変数 を宣言 します。const
を使用 することが一般 的 であり、これによりコードの読 みやすさが向上 し、最適 化 の機会 が増 えます。
-
外部 の変数 へのリンク:extern
キーワードや@extern
組 み込 み関数 を使用 して、他 のオブジェクトからエクスポートされた変数 とリンクすることができます。
-
識別子 :変数 の識別子 は、アルファベットまたはアンダースコアで始 まり、その後 に任意 の数 の英数字 またはアンダースコアが続 きます。また、キーワードとの重複 は許 されません(エスケープする方法 はあります)。⇒識別子
- コンテナレベルの
変数 :- コンテナレベルの
変数 は、静的 なライフタイムを持 ち、コンテナ内 で宣言 されるため、コンテナが評価 されると初期 化 されます。これらは、構造 体 、共用 体 、列挙 型 、または不透明 な型 の内部 で宣言 することができます。
- コンテナレベルの
-
静的 ローカル変数 :関数 内 でコンテナを使用 することで、静的 なローカル変数 を作成 することも可能 です。
- スレッドローカル
変数 :threadlocal
キーワードを使用 して、スレッドごとに異 なる変数 インスタンスを作成 することができます。
- ローカル
変数 :関数 内 やcomptime
ブロック内 で使用 される変数 は、ローカル変数 と呼 ばれます。これらは、関数 やブロックのスコープ内 でのみ有効 です。
- コンパイル
時 変数 :comptime
キーワードを使用 することで、コンパイル時 に値 がわかる変数 を定義 することができます。
Zig
型 を保持 するconst変数
[Zigでは
型 を保持 するconst変数 の例 const Complex = struct { real: f64, imag: f64, }; const Colour = enum { red, green, blue, }; const Number = union { int: i64, float: f64, };
構文
[構文 (EBNF)var-decl = ( "const" | "var" ) IDENTIFIER [ ":" type-expr ] [ byte-align ] [ link-section ] [ "=" expr ] ";"
- [21]
実際 は const は、型 が省略 でき、var は、型 が省略 できず、両方 とも初期 化 が必須 ないので[24]var-decl = const-var-decl | var-var-decl const-var-decl = "const" IDENTIFIER [ ":" type-expr ] [ byte-align ] [ link-section ] "=" expr ";" var-var-decl = "var" IDENTIFIER ":" type-expr [ byte-align ] [ link-section ] "=" expr ";"
- と
意味 論 も加味 するとなります。 - var
変数 の場合 は、不 定値 としてプリミティブ値 のundefined
で初期 化 することができます[25]。- undefinedは、
任意 の型 に強制 ( be coerced )することができます。一旦 これが起 こると、値 がundefinedであることを検出 することができなくなります。 undefinedは、値 が何 にでもなり得 ることを意味 し、型 によれば無意味 なものでさえもあり得 ることを意味 します。英語 で undefinedは "Not a meaningful value. Using this value would be a bug. The value will be unused, or overwritten before being used."(意味 のない値 。この値 を使 うとバグになる。この値 は使 われないか、使 われる前 に上書 きされるでしょう)という意味 です(ただしローカル変数 が使 われないと、error: unused local variable となります)。
- undefinedは、
const 変数
[キーワード const
で
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const i = 0; try stdout.print("i = {}\n", .{i}); }
実行 結果 i = 0
このコードは、const
キーワードを
const std = @import("std");
:std
という名前 で標準 ライブラリをインポートします。これにより、標準 ライブラリの機能 を使 うことができます。const stdout = std.io.getStdOut().writer();
:標準 出力 を表 すstdout
という名前 のライターを取得 します。これにより、プログラムは標準 出力 にテキストを書 き込 むことができます。pub fn main() !void {
:プログラムのエントリーポイントであるmain
関数 を宣言 します。この関数 は、戻 り値 としてエラーを返 す可能 性 があるため、!void
型 を返 します。pub
キーワードは、この関数 が外部 からアクセス可能 であることを示 します。const i = 0;
:i
という名前 の定数 を宣言 し、初期 値 として整数 0
を与 えます。この定数 は後 で使用 されます。try stdout.print("i = {}\n", .{i});
:標準 出力 に文字 列 を書 き込 むためにprint
メソッドを使用 します。"i = {}\n"
の部分 はフォーマット文字 列 であり、{}
の位置 にi
の値 が挿入 されます。.{i}
は、変数 i
の値 を挿入 するための特別 な構文 です。
var 変数
[キーワード var
で
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i : isize = 0; try stdout.print("i = {}\n", .{i}); i = 12; try stdout.print("i = {}\n", .{i}); i *= i; try stdout.print("i = {}\n", .{i}); }
実行 結果 i = 0 i = 12 i = 144
このコードは、var
キーワードを
const std = @import("std");
:標準 ライブラリをstd
としてインポートします。const stdout = std.io.getStdOut().writer();
:標準 出力 を表 すstdout
という名前 のライターを取得 します。pub fn main() !void {
:プログラムのエントリーポイントであるmain
関数 を宣言 します。この関数 は、エラーを返 す可能 性 があるため、!void
型 を返 します。var i : isize = 0;
:i
という名前 の変数 を宣言 し、型 としてisize
を指定 して初期 値 として整数 0
を与 えます。try stdout.print("i = {}\n", .{i});
:標準 出力 に文字 列 を書 き込 むためにprint
メソッドを使用 します。"i = {}\n"
の部分 はフォーマット文字 列 であり、{}
の位置 にi
の値 が挿入 されます。i = 12;
:変数 i
の値 を12
に変更 します。try stdout.print("i = {}\n", .{i});
:print
メソッドを使用 して、変更 後 のi
の値 を標準 出力 に出力 します。i *= i;
:変数 i
の値 を自乗 して再 代入 します。try stdout.print("i = {}\n", .{i});
:print
メソッドを使用 して、再 代入 後 のi
の値 を標準 出力 に出力 します。
変数 のシャドーイングは禁止
[外部 スコープの識別子 をシャドーイングすることは許 されませんconst x = 0; pub fn main() !void { var x : isize = 1; }
- コンパイル
結果 main.zig:3:9: error: local variable shadows declaration of 'x' main.zig:1:1: note: declared here (exit status 1)
エラーメッセージによれば、x
がローカルスコープで
識別子
[const @"identifier with spaces in it" = 0xff; const @"1SmallStep4Man" = 112358; const c = @import("std").c; pub extern "c" fn @"error"() void; pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int; const Color = enum { red, @"really red", }; const color: Color = .@"really red";
このコードは、Zigで
const @"identifier with spaces in it" = 0xff;
:スペースが含 まれる識別子 を使用 して、定数 を宣言 しています。Zigでは、ダブルクォートで囲 まれた文字 列 を識別子 として使用 することができます。const @"1SmallStep4Man" = 112358;
:数字 で始 まる識別子 を使用 して、定数 を宣言 しています。同様 に、ダブルクォートで囲 まれた文字 列 を識別子 として使用 しています。const c = @import("std").c;
:外部 ライブラリをインポートして、その一部 をc
という名前 で定数 として使用 します。これにより、外部 のCライブラリで定義 された型 や関数 にアクセスすることができます。pub extern "c" fn @"error"() void;
:外部 C関数 に識別子 を付 けて宣言 しています。extern "c"
はC言語 の呼 び出 し規約 を指定 し、void
は戻 り値 の型 を示 しています。pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
:同様 に、外部 C関数 に識別子 を付 けて宣言 しています。この関数 は引数 を取 り、c_int
型 を戻 り値 として返 します。const Color = enum { red, @"really red", };
:列挙 型 Color
を定義 しています。@"really red"
のように、列挙 子 にも識別子 を付 けることができます。const color: Color = .@"really red";
:Color
列挙 型 の変数 color
を宣言 し、@"really red"
の列挙 子 を初期 値 として指定 しています。
このように、Zigでは
整数
[整数 リテラル
[Zigでは、さまざまな
- 10
進数 :const decimal_int = 98222;
- 16
進数 :const hex_int = 0xff;
- 8
進数 :const octal_int = 0o755;
- 2
進数 :const binary_int = 0b11110000;
- アンダースコアを
使用 した視覚 的 な区切 り:const one_billion = 1_000_000_000;
構文 (EBNF)INTEGER = "0b" bin_int skip | "0o" oct_int skip | "0x" hex_int skip | dec_int skip skip = ([ \n] | line_comment)* bin = "0" | "1" bin_ = [ '_' ] bin oct = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" oct_ = [ '_' ] oct hex = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" | "A" | "B" | "C" | "D" | "E" | "F" hex_ = [ '_' ] hex dec = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" dec_ = [ '_' ] dec bin_int = bin bin_* oct_int = oct oct_* dec_int = dec dec_* hex_int = hex hex_*
- [21]。
- 2
進数 ・8進数 ・16進数 はそれぞれ0b
・0o
・0x
を前 置 します。他 の多 くの言語 と異 なり0
の次 の文字 は小文字 が必須 で、大文字 は受 け付 けません。
実行 時 の整数 値
[ただし、
fn divide(a: i32, b: i32) i32 { return a / b; }
このようなa
と b
は
演算 子
[+
や -
などの
- ラッピング
演算 子 :+%
および-%
- サチュレーティング
演算 子 :+|
および-|
Zigではi
または u
のi7
は7ビットの
- ラッピング
演算 およびサチュレーティング演算 とオーバーフローの例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i: i5 = 0; var j: i4 = 0; var k: i4 = 0; while (i < 20) { try stdout.print("i:{}, j:{}, k:{}\n", .{ i, j, k }); i = i + 1; j = j +% 1; k = k +| 1; } }
実行 結果 i:0, j:0, k:0 i:1, j:1, k:1 i:2, j:2, k:2 i:3, j:3, k:3 i:4, j:4, k:4 i:5, j:5, k:5 i:6, j:6, k:6 i:7, j:7, k:7 i:8, j:-8, k:7 i:9, j:-7, k:7 i:10, j:-6, k:7 i:11, j:-5, k:7 i:12, j:-4, k:7 i:13, j:-3, k:7 i:14, j:-2, k:7 i:15, j:-1, k:7 thread 1 panic: integer overflow /sandbox/src/main.zig:11:15: 0x21e650 in main (main) i = i + 1; ^ /usr/lib/zig/std/start.zig:574:37: 0x21e4fe in posixCallMainAndExit (main) const result = root.main() catch |err| { ^ /usr/lib/zig/std/start.zig:243:5: 0x21dfe1 in _start (main) asm volatile (switch (native_arch) { ^ ???:?:?: 0x0 in ??? (???) (exit status 139)
このZigのプログラムは、
var i: i5 = 0;
:i
という名前 の変数 を宣言 し、初期 値 として0
を与 えます。この変数 はi5
型 であり、5ビットの符号 付 き整数 を表 します。var j: i4 = 0;
:j
という名前 の変数 を宣言 し、初期 値 として0
を与 えます。この変数 はi4
型 であり、4ビットの符号 付 き整数 を表 します。var k: i4 = 0;
:k
という名前 の変数 を宣言 し、初期 値 として0
を与 えます。この変数 もi4
型 であり、4ビットの符号 付 き整数 を表 します。while (i < 20) { ... }
:i
の値 が20未満 の間 、ループが続 きます。i = i + 1;
:変数 i
の値 を1増加 させます。この演算 は、i5
型 の整数 の範囲 を超 える可能 性 があり、後 に整数 オーバーフローが発生 します。j = j +% 1;
:変数 j
の値 を1増加 させます。ここではラップアラウンド演算 子 +%
を使用 しています。これにより、j
の値 が範囲 外 になる場合 は0に戻 ります。k = k +| 1;
:変数 k
の値 を1増加 させます。ここでは飽和 演算 子 +|
を使用 しています。これにより、k
の値 が範囲 外 になる場合 は最大 値 に飽和 します。
プログラムのi
のi5
j
のk
の
浮動 小数点 数
[浮動 小数点 数 の種類
[Zigでは、
- f16:IEEE-754-2008バイナリ16
- f32:IEEE-754-2008バイナリ32
- f64:IEEE-754-2008バイナリ64
- f80:IEEE-754-2008 80ビット
拡張 精度 - f128:IEEE-754-2008バイナリ128
- c_longdouble:
対象 C ABIのlong doubleに対応
浮動 小数点 数 リテラル
[const floating_point = 123.0E+77; const another_float = 123.0; const yet_another = 123.0e+77; const hex_floating_point = 0x103.70p-5; const another_hex_float = 0x103.70; const yet_another_hex_float = 0x103.70P-5;
構文 (EBNF)FLOAT = "0x" hex_int "." hex_int [ ( "p" | "P" ) [ "-" | "+" ] dec_int ] skip | dec_int "." dec_int [ ( "e" | "E" ) [ "-" | "+" ] dec_int ] skip | "0x" hex_int ( "p" | "P" ) [ "-" | "+" ] dec_int skip | dec_int ( "e" | "E" ) [ "-" | "+" ] dec_int skip
- [21]。
- 10
進数 のほか、16進数 の浮動 小数点 数 リテラルに対応 していますが、2進数 ・8進数 の浮動 小数点 数 リテラルには対応 していません。 - 16
進数 の浮動 小数点 数 リテラルも、指数 部 は10進数 です。
特殊 な浮動 小数点 数値
[NaN、
const std = @import("std"); const inf = std.math.inf(f32); const negative_inf = -std.math.inf(f64); const nan = std.math.nan(f128);
浮動 小数点 数 の演算
[デフォルトでは、
export fn foo_strict(x: f64) f64 { return x + big - big; } export fn foo_optimized(x: f64) f64 { @setFloatMode(.Optimized); return x + big - big; }
演算 子
[Zigの
加算 と減算
[加算 :a + b
、a += b
減算 :a - b
、a -= b
- ラップ
加算 :a +% b
、a +%= b
- ラップ
減算 :a -% b
、a -%= b
飽和 加算 :a +| b
、a +|= b
飽和 減算 :a -| b
、a -|= b
乗算 と除算
[乗算 :a * b
、a *= b
除算 :a / b
、a /= b
- ラップ
乗算 :a *% b
、a *%= b
飽和 乗算 :a *| b
、a *|= b
剰余 除算 :a % b
、a %= b
ビット演算
[- ビットシフト:
左 シフトa << b
、右 シフトa >> b
- ビット AND:
a & b
、a &= b
- ビット OR:
a | b
、a |= b
- ビット XOR:
a ^ b
、a ^= b
- ビット NOT:
~a
論理 演算
[論理 AND:a and b
論理 OR:a or b
論理 NOT:!a
比較 演算 子
[等 しい:a == b
等 しくない:a != b
大 なり:a > b
大 なりイコール:a >= b
小 なり:a < b
小 なりイコール:a <= b
オプショナルとエラー処理
[- オプショナルのデフォルト
値 取得 :a orelse b
- オプショナルのアンラップ:
a.?
- エラー
処理 のデフォルト値 取得 :a catch b
、a catch |err| b
その他
[- アドレス
取得 :&a
- ポインタの
参照 :a.*
制御 構造
[Zigは、やや
分岐
[Zigには、#if と #switch の
if
[Zigでは、if
は
構文 (EBNF)[21]if-expr = if-prefix expr [ "else" [ payload ] expr ] if-prefix = "if" "(" expr ")" [ ptr-payload ] payload = "|" IDENTIFIER "|" ptr-payload = "|" [ "*" ] IDENTIFIER "|" if-statement = if-prefix block-expr [ "else" [ payload ] statement ] | if-prefix assign-expr ( ”;” | "else" [ payload ] statement ) block-expr = [ block-label ] block block-label = IDENTIFIER ":" block = "{" statement* "}" assign-expr = expr [ assign-op expr ] assign-op = "+=" | "-=" | "*=" | "/=" ...(
略 )
- if
式 の例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const i = 0; if (i == 0) try stdout.writeAll("zero\n") // ここに ; があるとエラーになります。 else try stdout.writeAll("non zero\n"); try stdout.print(if (i == 0) "ゼロ\n" else "
非 ゼロ\n", .{}); }実行 結果 zero ゼロ
- 8
行 目 に ; があると if式 がそこで終 わってしまい、else と結合 できません。ブロックを使 えば… - if
文 に変更 if (i == 0) { try stdout.writeAll("zero\n"); // ブロック
中 ならば ; があってもエラーになりません。 } else { try stdout.writeAll("non zero\n"); }- と ; を
使 うことができます[27]。
条件 不成立 でelseを省略 すると
[ifvoid
となります。
条件 不成立 でelseを省略 するとconst std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { try stdout.print("if (false) 1 => {}\n", .{ if (false) 1 }); }
実行 結果 if (false) 1 => void
条件 式 に整数 を使 うとconst std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const i = 0; if (i) { try stdout.writeAll("not zero"); } }
オプショナル型 を条件 としたif
[ifの
- nullable-if.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var a: ?u16 = 42; if (a) |*value| { try stdout.print("value.* = {}({})\n", .{value.*, @TypeOf(value.*)}); value.* = 123; } else { unreachable; } if (a) |n| { try stdout.print("n = {}({})\n", .{n, @TypeOf(n)}); } else { unreachable; } a = null; if (a) |_| { unreachable; } else { try stdout.print("a = {?}({})\n", .{a, @TypeOf(a)}); } }
実行 結果 value.* = 42(u16) n = 123(u16) a = null(?u16)
このコードでは、a
という42
で
a
がnull
でない|*value|
value
に123
に
2a
がnull
でない|n|
の
a
にnull
をa
がnull
であるかどうかをチェックし、null
のelse
ブロック|_|
のa
がnull
であることを
エラーユニオン型 を条件 としたif
[ifの
- errorunion-if.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var b: anyerror!u32 = error.BadValue; try stdout.print("b = {any}({})\n", .{b, @TypeOf(b)}); if (b) |_| { unreachable; } else |err| { try stdout.print("err = {?}({})\n", .{err, @TypeOf(err)}); } b = 4423; try stdout.print("b = {any}({})\n", .{b, @TypeOf(b)}); if (b) |n| { try stdout.print("n = {}({})\n", .{n, @TypeOf(n)}); } else |_| { unreachable; } }
実行 結果 b = error.BadValue(anyerror!u32) err = error.BadValue(anyerror) b = 4423(anyerror!u32) n = 4423(u32)
このコードでは、b
というerror.BadValue
というエラーコードを
b
がエラーコードをelse |err|
ブロック|err|
の
2b
がエラーコードをelse |_|
ブロック|n|
の
switch
[Zigでは、switch
は
構文 (EBNF)- [21]
switch-expr = "switch" "(" expr ")" "{" switch-prong-list "}" switch-prong-list = (switch-prong "," )* [ switch-prong ] switch-prong = switch-case "=>" [ ptr-payload ] assign-expr switch-case = switch-item ( "," switch-item )* [ "," ] | "else" switch-item = expr [ "..." expr ]
- switch.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const ary = .{1, 'Z', 3.14, .{1,2,3}, true, void, null, i64}; inline for (ary) |obj| { switch (@typeInfo(@TypeOf(obj))) { .ComptimeInt => try stdout.print("ComptimeInt: {}\n", .{obj}), .ComptimeFloat => try stdout.print("ComptimeFloat: {}\n", .{obj}), .Struct => try stdout.print("Struct: {}\n", .{obj}), .Bool => try stdout.print("Bool: {}\n", .{obj}), .Type => try stdout.print("Type: {}\n", .{obj}), .Null => try stdout.print("Null: {}\n", .{obj}), else => try stdout.print("{}\n", .{@typeInfo(@TypeOf(obj))}) } } }
実行 結果 ComptimeInt: 1 ComptimeInt: 90 ComptimeFloat: 3.14e+00 Struct: { 1, 2, 3 } Bool: true Type: void Null: null Type: i64
反復
[Zigには、#while と #for の
while
[while
はwhile
にはelse
break
やcontinue
も
構文 (EBNF)loop-expr = [ "inline" ] ( for-expr | while-expr ) while-expr = while-prefix expr [ "else" [ payload ] expr ] while-prefix = "while" "(" expr ")" [ ptr-payload ] [ while-continue-expr ] while-continue-expr = ":" "(" assign-expr ")" while-statement = while-prefix block-expr [ "else" [ pay-load ] statement ] | while-prefix assign-expr ( ”;” | "else" [ payload ] statement )
多 くの構文 要素 はif
と共通 しているので #if の構文 も参照 してください[21]。- Zigも、pythonのように else を
伴 うことのできる while です。
whileの例
[- while.zig
pub fn main() !void { var i: usize = 1; while (i < 50) : (i += 1) { try stdout.print("{}! == {}\n", .{ i, fib(i) }); } } fn fib(n: usize) usize { return if (n < 2) n else fib(n - 1) + fib(n - 2); } const stdout = std.io.getStdOut().writer(); const std = @import("std");
実行 結果 An error occurred: 1! == 1 2! == 1 3! == 2 4! == 3 5! == 5 6! == 8 7! == 13 8! == 21 9! == 34 10! == 55 11! == 89 12! == 144 13! == 233 14! == 377 15! == 610 16! == 987 17! == 1597 18! == 2584 19! == 4181 20! == 6765 21! == 10946 22! == 17711 23! == 28657 24! == 46368 25! == 75025 26! == 121393 27! == 196418 28! == 317811 29! == 514229 30! == 832040 31! == 1346269 32! == 2178309 33! == 3524578 34! == 5702887 35! == 9227465 36! == 14930352 37! == 24157817 38! == 39088169 39! == 63245986
while (i < 50) : (i += 1) { try stdout.print("{}! == {}\n", .{ i, fib(i) }); }
- は
while (i < 50) { try stdout.print("{}! == {}\n", .{ i, fib(i) }); i += 1; }
- と
等価 で、追加 の式 はC言語 の for (;;) の三 項目 にあたります。
elseを伴 ったwhileの例
[- while-with-else.zig
const stdout = std.io.getStdOut().writer(); const std = @import("std"); pub fn main() !void { var i: usize = 0; while (i < 5) : (i += 1) { try stdout.print("{} ", .{i}); } else { try stdout.writeAll("done!\n"); } i = 0; while (i < 50) : (i += 1) { try stdout.print("{} ", .{i}); if (i == 10) { try stdout.writeAll("break!\n"); break; } } else { try stdout.writeAll("done!\n"); } }
実行 結果 0 1 2 3 4 done! 0 1 2 3 4 5 6 7 8 9 10 break!
- while のループを「
完走 」すると、else以降 が実行 されます。 - もし break などで
中断 されると、else以降 は実行 されません。
では、このelse
- 1000
以下 の素数 を求 める(フラッグ版 ) const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i: usize = 2; while (i <= 1000) : (i += 1) { var j: usize = 2; var is_prime = true; while (j * j <= i) : (j += 1) { if (i % j == 0) { is_prime = false; break; } } if (is_prime) try stdout.print("{} ", .{i}); } }
- break で
抜 けたかをフラッグ is_prime で判断 していますが、 - 1000
以下 の素数 を求 める(else版 ) const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i: usize = 2; while (i <= 1000) : (i += 1) { var j: usize = 2; while (j * j <= i) : (j += 1) { if (i % j == 0) { break; } } else try stdout.print("{} ", .{i}); } }
- フラッグがなくなり
簡素 になりました。
ラベル付 きwhile
[whileループにラベルを
- ラベル
付 きwhile const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i: u16 = 0; var j: u16 = 0; outer: while (i < 15) : (i += 1) { j = 0; while (j < 15) : (j += 1) { if (i == 7 and j == 11) { break :outer; } } } try stdout.print("i = {}, j = {}\n", .{ i, j }); }
実行 結果 i = 7, j = 11
オプショナル型 を条件 としたwhile
[ifと
while
- オプショナル
型 を条件 としたwhile const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var sum: u32 = 0; while (sequence()) |n| { sum += n; } try stdout.print("sum = {}\n", .{sum}); } fn sequence() ?u32 { const S = struct { var x: u32 = 5; }; return if (S.x == 0) null else blk: { S.x -= 1; break :blk S.x; }; }
実行 結果 sum = 10
エラーユニオン型 を条件 としたwhile
[ifと
while
while
- エラーユニオン
型 を条件 としたwhile const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var sum: u32 = 0; while (sequence()) |n| { sum += n; } else |err| { try stdout.print("err = {}\n", .{err}); } try stdout.print("sum = {}\n", .{sum}); } fn sequence() !u32 { const S = struct { var x: u32 = 5; }; return if (S.x == 0) error.ReachedZero else blk: { S.x -= 1; break :blk S.x; }; }
実行 結果 err = error.ReachedZero sum = 10
inline while
[whileループはインライン
[TODO:コード
for
[Zigのfor
ループは、スライスやcontinue
やbreak
をfor
ループをelse
構文 (EBNF)for-statement = "for" for-prefix block-expr [ "else" statement ] for-expr = "for" for-prefix expr [ "else" expr ] for-prefix = "(" for-arguments-list ")" ptr-list-payload for-arguments-list = for-item { "," for-item } [ "," ] for-item = expr [ ".." expr] ptr-list-payload = "|" [ "*" ] IDENTIFIER { "," [ "*" ] IDENTIFIER } [ "," ] "|"
- [21]。
- for-ary.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const int = i16; const ary = [_]int{ 1, 5, 4, 6, 4, 9 }; var sum: int = 0; for (ary, 0..) |n, i| { try stdout.print("n = {}, i = {}\n", .{ n, i }); sum += n; } try stdout.print("sum = {}\n", .{sum}); var ary2 = ary; for (&ary2) |*r| { r.* += 10; } for (ary2) |n| { try stdout.print("{}, ", .{n}); } }
実行 結果 n = 1, i = 0 n = 5, i = 1 n = 4, i = 2 n = 6, i = 3 n = 4, i = 4 n = 9, i = 5 sum = 29 11, 15, 14, 16, 14, 19,
- for-str.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { const str = "simple string"; for (str) |c| { try stdout.print("{c} ", .{c}); } }
実行 結果 s i m p l e s t r i n g
- for-range.zig
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { for (23..43) |i| { try stdout.print("{} ", .{i}); } }
実行 結果 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
ラベル付 きfor
[ラベル
- ラベル
付 きfor const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var x: usize = undefined; var y: usize = undefined; loop_top: for ("Hello", 0..) |co, i| { for ("World", 0..) |ci, j| { if (co == ci) { try stdout.print("c = '{c}'({}, {})\n", .{ ci, i, j }); x = i; y = j; break :loop_top; } } } try stdout.print("x = {}, y = {}\n", .{ x, y }); }
実行 結果 c = 'l'(2, 3) x = 2, y = 3
inline for
[Forループはインライン
- inline for
const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var x: usize = undefined; var y: usize = undefined; loop_top: inline for ("Hello", 0..) |co, i| { inline for ("World", 0..) |ci, j| { if (co == ci) { try stdout.print("c = '{c}'({}, {})\n", .{ ci, i, j }); x = i; y = j; break :loop_top; } } } try stdout.print("x = {}, y = {}\n", .{ x, y }); }
実行 結果 c = 'l'(2, 3) x = 2, y = 3
関数
[Zigの
Zigの
構文 (EBNF)top-level-decl = [ "inline" | "noinline" ] FnProto ( ";" | block ) FnProto = "fn" [ IDENTIFIER ] "(" param-decl-list ")" [ byte-align ] [ link-section ] [ call-conv ] [ "!" ] type-expr param-decl-list = ( param-decl "," )* [ param-decl ] param-decl = [ doc_comment ] [ "noalias" | "comptime" ] [ IDENTIFIER ":" ] param-type | "..." param-type = "anytype" | type-expr type-expr = [ prefix-type-op ] ErrorUnionExpr prefix-type-op = "?" | "anyframe" "->" | slice-type-start ( byte-align | "const" | "volatile" | "allowzero" )* | ptr-type-start ( "align" "(" expr [ ":" INTEGER ":" INTEGER) ] ")" | "const" | "volatile" | "allowzero" )* | array-type-start slice-type-start = "[" [ ":" expr ] "]" byte-align = "align" "(" expr ")" link-section = "linksection" "(" expr ")" call-conv = "callconv" "(" expr ")" ptr-type-start = "*" | "**" | "[" "*" [ "c" | ":" expr ] "]" array-type-start = "[" expr [ ":" expr ] "]"
- top-level-declは
関数 宣言 の他 、変数 宣言 なども含 みますが、関数 宣言 部分 を抜粋 しました[21]。 関数 の例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { try stdout.print("{}\n", .{div(1, 0)}); try stdout.print("{}\n", .{div(0, 0)}); } fn div(f1: f64, f2: f64) f64 { return f1 / f2; }
実行 結果 inf nan
他 のプログラミング言語 をご存 じの方 なら、関数 div() が前方 参照 になっているのは大丈夫 なのか?と思 われるかもしれません。- Zigではトップレベルの
識別子 は処理 系 が参照 解決 (とシグネチャーを含 めた型 の一致 の確認 )を引受 けてくれます。
基本 的 な関数 の定義 : Zigでの関数 の定義 は、以下 のように行 います。fn add(a: i8, b: i8) i8 { return a + b; }
- この
例 では、add
という関数 が定義 されており、2つのi8
型 の引数 を受 け取 り、i8
型 の値 を返 します。
外部 関数 の定義 : Zigでは、外部 ライブラリやAPIから関数 を利用 するために、extern
キーワードを使用 して外部 関数 を宣言 します。extern "kernel32" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
- この
例 では、Windowsのkernel32
ライブラリからExitProcess
という関数 を使用 しています。
- パラメータの
型 推論 :関数 のパラメータの型 は、anytype
を使用 して関数 が呼 び出 されるときに推論 されます。fn addFortyTwo(x: anytype) @TypeOf(x) { return x + 42; }
- この
例 では、addFortyTwo
関数 のパラメータx
の型 が呼 び出 し時 に推論 されます。
- インライン
関数 :inline
キーワードを使用 することで、関数 をコールサイトにインライン展開 することができます。inline fn foo(a: i32, b: i32) i32 { return a + b; }
- この
例 では、foo
関数 がインラインで展開 され、コンパイル時 に計算 が行 われます。
関数 ポインタ:関数 を値 として扱 うために、関数 ポインタを使用 することができます。const Call2Op = *const fn (a: i8, b: i8) i8; fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 { return fnCall(op1, op2); }
- この
例 では、Call2Op
型 の関数 ポインタを引数 として受 け取 り、その関数 を呼 び出 します。
Zigの
エラー
[Zigでは、エラー( Errors )も
エラー集合 型
[エラーenum
のようなものです。
構文 (EBNF)error-set-decl = "error" "{" identifier-list "}"
- [21]
- エラー
集合 型 の例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); const FileOpenError = error { AccessDenied, OutOfMemory, FileNotFound, }; const AllocationError = error { OutOfMemory, }; pub fn main() !void { try stdout.print("AllocationError.OutOfMemory == FileOpenError.OutOfMemory ⇒ {}\n", .{AllocationError.OutOfMemory == FileOpenError.OutOfMemory}); }
実行 結果 AllocationError.OutOfMemory == FileOpenError.OutOfMemory ⇒ true
エラー集合 のマージ
[エラー||
グローバルエラー集合
[anyerror は、グローバルエラー
グローバルエラー
エラーユニオン型
[エラー!
で
var error_or_value: AllocationError ! u16 = 10;
エラーユニオン
構文 (EBNF)error-union-expr = suffix-expr ( "!" TypeExpr )? suffix-expr = "async" PrimaryTypeExpr SuffixOp* FnCallArguments | PrimaryTypeExpr ( SuffixOp | FnCallArguments )*
- [21]
演算 子
[Zigには、
演算 子 一覧 表
[コード | |||
---|---|---|---|
a + b
a += b
|
「@addWithOverflow」も
|
2 + 5 == 7
| |
a +% b
a +%= b
|
「@addWithOverflow」も
|
@as(u32, std.math.maxInt(u32)) +% 1 == 0
| |
a +| b
a +|= b
|
|
@as(u32, std.math.maxInt(u32)) +| 1 == @as(u32, std.math.maxInt(u32))
| |
a - b
a -= b
|
「@subWithOverflow」も
|
2 - 5 == -3
| |
a -% b
a -%= b
|
「@subWithOverflow」も
|
@as(u32, 0) -% 1 == std.math.maxInt(u32)
| |
a -| b
a -|= b
|
|
@as(u32, 0) -| 1 == 0
| |
-a
|
|
-1 == 0 - 1
| |
-%a
|
|
-%@as(i32, std.math.minInt(i32)) == std.math.minInt(i32)
| |
a * b
a *= b
|
「@mulWithOverflow」も
|
2 * 5 == 10
| |
a *% b
a *%= b
|
「@mulWithOverflow」も
|
@as(u8, 200) *% 2 == 144
| |
a *| b
a *|= b
|
* |
|
@as(u8, 200) *| 2 == 255
|
a / b
a /= b
|
10 / 5 == 2
| ||
a % b
a %= b
|
|
10 % 3 == 1
| |
a << b
a <<= b
|
* |
|
1 << 8 == 256
|
a <<| b
a <<|= b
|
* |
|
@as(u8, 1) <<| 8 == 255
|
a >> b
a >>= b
|
* |
「@shrExact」も
|
10 >> 1 == 5
|
a & b
a &= b
|
|
0b011 & 0b101 == 0b001
| |
a | b
a |= b
|
|
0b010 | 0b100 == 0b110
| |
a ^ b
a ^= b
|
|
0b011 ^ 0b101 == 0b110
| |
~a
|
|
~@as(u8, 0b10101111) == 0b01010000
| |
a orelse b
|
もし a がnull ならば、b ("デフォルトa のb がnoreturn |
const value: ?u32 = null;
const unwrapped = value orelse 1234;
unwrapped == 1234
| |
a.?
|
|
const value: ?u32 = 5678;
value.? == 5678
| |
a catch b
a catch |err| b
|
もし a がerror ならば、b ("デフォルトa のerr はエラーであり,b のスコープ |
const value: anyerror!u32 = error.Broken;
const unwrapped = value catch 1234;
unwrapped == 1234
| |
a and b
|
|
(false and true) == false
| |
a or b
|
|
(false or true) == true
| |
!a
|
!false == true
| ||
a == b
|
a と b がtrue を、そうでないfalse を |
(1 == 1) == true
| |
a == null
|
a が null のtrue を、そうでないfalse を |
const value: ?u32 = null;
value == null
| |
a != b
|
a と b がfalse を、そうでないtrue を |
(1 != 1) == false
| |
a > b
|
a が b よりtrue を、そうでないfalse を |
(2 > 1) == true
| |
a >= b
|
a が b よりtrue を、そうでないfalse を |
(2 >= 1) == true
| |
a < b
|
a が b よりtrue を、そうでないfalse を |
(1 < 2) == true
| |
a <= b
|
a が b よりtrue を、そうでないfalse を |
(1 <= 2) == true
| |
a ++ b
|
|
const mem = @import("std").mem;
const array1 = [_]u32{1,2};
const array2 = [_]u32{3,4};
const together = array1 ++ array2;
mem.eql(u32, &together, &[_]u32{1,2,3,4})
| |
a ** b
|
|
const mem = @import("std").mem;
const pattern = "ab" ** 3;
mem.eql(u8, pattern, "ababab")
| |
a.*
|
|
const x: u32 = 1234;
const ptr = &x;
ptr.* == 1234
| |
&a
|
すべて |
|
const x: u32 = 1234;
const ptr = &x;
ptr.* == 1234
|
a || b
|
|
const A = error{One};
const B = error{Two};
(A || B) == error{One, Two}
|
オーバーフロー
[Zigでは、
- オーバーフローの
例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var r :i3 = 0; while (r < 10) : (r += 1){ try stdout.print("{} ", .{r}); } }
実行 結果 An error occurred: 0 1 2 3 thread 7015 panic: integer overflow /tmp/playground2097195080/play.zig:6:25: 0x22cff6 in main (play) while (r < 10) : (r += 1){ ^ /usr/local/bin/lib/std/start.zig:561:37: 0x22657a in std.start.callMain (play) const result = root.main() catch |err| { ^ /usr/local/bin/lib/std/start.zig:495:12: 0x20716e in std.start.callMainWithArgs (play) return @call(.{ .modifier = .always_inline }, callMain, .{}); ^ /usr/local/bin/lib/std/start.zig:409:17: 0x206206 in std.start.posixCallMainAndExit (play) std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); ^ /usr/local/bin/lib/std/start.zig:322:5: 0x206012 in std.start._start (play) @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); ^
符号 付 き3ビット整数 の r を r < 10 の間 インクリメントしています。符号 付 き3ビット整数 は、 -4 ... 3 の値 しか表 せず 4 になるとオーバーフローします。- C
言語 などでは、無限 ループになりますが Zig ではランタイムに検出 されます。
飽和 演算
[Zigには、
飽和 演算 の例 const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var i: i5 = 0; var sum: i5 = 0; while (i < 10) : (i += 1) { sum +|= i; try stdout.print("i = {}, sum = {}\n", .{ i, sum }); } }
実行 結果 i = 0, sum = 0 i = 1, sum = 1 i = 2, sum = 3 i = 3, sum = 6 i = 4, sum = 10 i = 5, sum = 15 i = 6, sum = 15 i = 7, sum = 15 i = 8, sum = 15 i = 9, sum = 15
優先 順位
[ x() x[] x.y x.* x.?
a!b
x{}
!x -x -%x ~x &x ?x
* / % ** *% *| ||
+ - ++ +% -% +| -|
<< >> <<|
& ^ | orelse catch
== != < > <= >=
and
or
= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |=
オプショナル型
[Zigには「オプショナル
- オプショナル
型 値 としてnullを受 け入 れられる。非 オプショナル型 値 としてnullを受 け入 れられない。
ここまでに
オプショナル?
と
var a: i32 = null; //
非 オプショナル型 を null で初期 化 するとエラーになる! var b: ?i32 = null; // オプショナル型 は null で初期 化 してもエラーにならない
アンラップ
[オプショナル
- ifを
使 ったアンラップ const std = @import("std"); const stdout = std.io.getStdOut().writer(); pub fn main() !void { var s: ?[]const u8 = null; if (s) |ss| { try stdout.print("{s}\n", .{ss}); } else { try stdout.writeAll("it's null\n"); } s = "abc"; if (s) |ss| { try stdout.print("{s}\n", .{ss}); } else { try stdout.writeAll("it's null\n"); } }
実行 結果 it's null abc
配列
[Zigの
配列 の定義
配列 リテラル:const array = [_]u8{'h', 'e', 'l', 'l', 'o'}
配列 サイズの取得 :array.len
配列 の要素 アクセス:array[index]
配列 の操作
配列 の反復 処理 :for (array) |item| { ... }
配列 の変更 :array[index] = value
配列 の結合 :const concatenated = array1 ++ array2
- コンパイル
時 初期 化
- コンパイル
時 に配列 の初期 化 が可能 - コンパイル
時 コードでの配列 操作
多次元 配列
配列 のネストにより多次元 配列 を作成 可能 多次元 配列 の反復 処理
- センチネル
終端 配列
- センチネル
終端 配列 の定義 - センチネル
終端 配列 の操作
Zigの
構文 (EBNF)array-type-start = "[" expr [ ":" expr ] "]"
- [21]
配列 と特殊 な演算 子 ++ と **
[配列 のコード例 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { const ary = [_]i8{ 2, 3, 5, 7 }; try stdout.print("ary == {}{{ ", .{@TypeOf(ary)}); for (ary) |elm| { try stdout.print("{}, ", .{elm}); } try stdout.writeAll("}\n"); try stdout.print("ary ++ ary == {}{{ ", .{@TypeOf(ary ++ ary)}); for (ary ++ ary) |elm| { try stdout.print("{}, ", .{elm}); } try stdout.writeAll("}\n"); try stdout.print("ary ** 3 == {}{{ ", .{@TypeOf(ary ** 3)}); for (ary ** 3) |elm| { try stdout.print("{}, ", .{elm}); } try stdout.writeAll("}\n"); }
実行 結果 ary == [4]i8{ 2, 3, 5, 7, } ary ++ ary == [8]i8{ 2, 3, 5, 7, 2, 3, 5, 7, } ary ** 3 == [12]i8{ 2, 3, 5, 7, 2, 3, 5, 7, 2, 3, 5, 7, }
省略 記法 const ary = [_]i8{ 2, 3, 5, 7 };
- は
完全 な表記 const ary: [4]i8= [4]i8{ 2, 3, 5, 7 };
- ですが、
型 は型 推定 で、要素 数 もリテラルなので _ と書 けます。 配列 連結 演算 子 ary ++ ary
++
は配列 と配列 を連結 した新 しい配列 を返 す演算 子 です。配列 連結 演算 子 ary ** 3
**
は配列 を回数 だけ繰返 した新 しい配列 を返 す演算 子 です。ary ** ary
はary ++ ary ++ ary
と等価 です。
多次元 配列
[多次元 配列 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { var matrix: [16][16]i32 = undefined; //
全 成分 を 0 に for (&matrix) |*col| { for (&col.*) |*value| { value.* = 0; } } try stdout.print("martix == {}{{\n", .{@TypeOf(matrix)}); for (matrix) |col| { try stdout.writeAll(" { "); for (col) |value| { try stdout.print("{}, ", .{value}); } try stdout.writeAll("},\n"); } try stdout.writeAll("}\n\n"); //対 角 成分 を 1 に for (&matrix, 0..) |*col, ci| { for (&col.*, 0..) |*value, ri| { if (ci == ri) { value.* = 1; } } } try stdout.print("martix == {}{{\n", .{@TypeOf(matrix)}); for (matrix) |col| { try stdout.writeAll(" { "); for (col) |value| { try stdout.print("{}, ", .{value}); } try stdout.writeAll("},\n"); } try stdout.writeAll("}\n"); }実行 結果 martix == [16][16]i32{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, } martix == [16][16]i32{ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, }, }
多次元 配列 の宣言 var matrix : [16][16]i32 = undefined;
- undefined で
初期 化 しています。というより初期 化 していません。 - スカラで
配列 の初期 化 はできません var matrix : [16][16]i32 = 0; // これはエラーになる
- と
書 きたいところですができないので、二 重 のforループで初期 化 しています。
センチネル終端 配列
[センチネル[
の x で
- センチネル
終端 配列 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { const ary = [_:-1]i32{ 1, 4, 9, 16, 25 }; try stdout.print("@TypeOf(ary) ⇒ {}\n", .{@TypeOf(ary)}); var i: u16 = 0; while (i <= ary.len) : (i += 1) { try stdout.print("{} ", .{ary[i]}); } }
実行 結果 @TypeOf(ary) ⇒ [5:-1]i32 1 4 9 16 25 -1
ベクトル
[ベクトルは、bool、
ベクトルは、
算術 演算 - +, -, /, *, @divFloor, @sqrt, @ceil, @log, など
- ビット
演算 子 - >>, <<, &, |, ~, など
比較 演算 子 - <, >, ==, など
スカラー(
Zigは,
ターゲットマシンのネイティブ SIMD サイズより
- ベクトル
演算 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { const a = @Vector(4, i32){ 1, 2, 3, 4 }; const b = @Vector(4, i32){ 5, 6, 7, 8 }; try stdout.print(" {}\n+ {}\n= {}\n\n", .{ a, b, a + b }); try stdout.print(" {}\n- {}\n= {}\n\n", .{ a, b, a - b }); try stdout.print(" {}\n* {}\n= {}\n\n", .{ a, b, a * b }); try stdout.print(" {}\n/ {}\n= {}\n\n", .{ a, b, a / b }); try stdout.print(" {}\n% {}\n= {}\n\n", .{ a, b, a % b }); }
実行 結果 { 1, 2, 3, 4 } + { 5, 6, 7, 8 } = { 6, 8, 10, 12 } { 1, 2, 3, 4 } - { 5, 6, 7, 8 } = { -4, -4, -4, -4 } { 1, 2, 3, 4 } * { 5, 6, 7, 8 } = { 5, 12, 21, 32 } { 1, 2, 3, 4 } / { 5, 6, 7, 8 } = { 0, 0, 0, 0 } { 1, 2, 3, 4 } % { 5, 6, 7, 8 } = { 1, 2, 3, 4 }
[TODO:
ポインター
[Zigには、
*T
-正確 に1つの項目 への単一 項目 ポインター。- deref
構文 をサポート:ptr.*
- deref
[*]T
-未知数 のアイテムへの多項目 ポインター。- インデックス
構文 をサポート:ptr[i]
- スライス
構文 をサポート:ptr[start..end]
- ポインター
演算 をサポート:ptr + x
,ptr - x
T
は既知 のサイズでなければならず、anyopaqueや他 の opaque型 にはできません。
- インデックス
*[N]T
- N個 のアイテムへのポインター、配列 への単 項目 ポインターと同 じです。- インデックス
構文 に対応 :array_ptr[i]
- スライス
構文 に対応 :array_ptr[start..end]
- lenプロパティをサポート:
array_ptr.len
- インデックス
[]T
- スライス (ファットポインター。[*]T型 のポインターと長 さを含 みます)。- インデックス
構文 に対応 :slice[i]
- スライス
構文 に対応 :slice[start..end]
- lenプロパティをサポート:
slice.len
- インデックス
単一 項目 ポインター
[多項目 ポインター
[[TODO]
volatile
[ロードとストアは、
アライメント
[それぞれの
アラインメントはCPUアーキテクチャに1 << 29
より
Zigでは、ポインター
align
[align
は、ポインターのアライメントを
allowzero
[allowzeroポインター
const
[constポインター
センチネル終端 ポインター
[センチネル[*:x]T
は、センチネル
スライス
[スライス( Slices )はポインターと
- スライスを
使 ったコード例 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { var array = [_]i32{ 2, 3, 5, 7, 11, 13, 17 }; var q: usize = 1; const slice = array[q .. array.len -1]; try stdout.print("@TypeOf(slice) ⇒ {}\n", .{@TypeOf(slice)}); for (slice) |x| { try stdout.print("{} ", .{x}); } }
実行 結果 @TypeOf(slice) ⇒ []i32 3 5 7 11 13
センチネル終端 スライス
[センチネル
- スライスを
使 ったコード例 const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { const slice: [:0]const u8 = "hello"; try stdout.print("@TypeOf(slice) ⇒ {}\n", .{@TypeOf(slice)}); for (slice) |ch| { try stdout.print("{c} ", .{ch}); } }
実行 結果 @TypeOf(slice) ⇒ [:0]const u8 h e l l o
コンテナー
[コンテナー(Containers)とは、
コンテナーは、
[TODO:std.containerについて]
構文 (EBNF)container-members = container-declarations ( container-field "," )* (container-field | container-declarations) container-declarations = TestDecl container-declarations | TopLevelComptime container-declarations | [ doc_comment ] [ "pub" ] TopLevelDecl container-declarations | container-field = [ doc_comment ] [ "comptime" ] IDENTIFIER [ ":" ( "anytype" | TypeExpr) [ ByteAlign ] ] [ "=" expr ] container-decl-auto = container-decl-type "{" [ container_doc_comment ] container-members "}" container-decl-type = "struct" | "opaque" | "enum" [ "(" expr ")" ] | "union" [ "(" ( "enum" [ "(" expr ")" ] ) | expr ")"
- コンテナー
関連 の構文 を抜粋 [21]
struct
[struct(
- struct の
定義 const stdout = @import("std").io.getStdOut().writer(); const Complex = struct { real: f64, imag: f64, }; pub fn main() !void { try stdout.print("@TypeOf(Complex) ⇒ {}\n", .{@TypeOf(Complex)}); }
実行 結果 @TypeOf(Complex) ⇒ type
- 3-6
行 目 が struct の宣言 で、変数 に保存 しています。 - struct で
宣言 した値 の型 は type です。 - ところで Zigはフィールドの
順番 とサイズを保証 しません。しかし、フィールドはABIアラインされていることが保証 されています[51]。 - もし、
順番 とサイズを保証 したい場合 キーワード packed を前 置 します。 順番 とサイズを保証 したい場合 const Complex = packed struct { real: f64, imag: f64, };
- インスタンスの
生成 const stdout = @import("std").io.getStdOut().writer(); const Complex = struct { real: f64, imag: f64, }; pub fn main() !void { const cplx = Complex{ .real = 1.2, .imag = 4.1, }; try stdout.print("{}\n", .{ cplx }); }
実行 結果 Complex{ .real = 1.2e+00, .imag = 4.1e+00 }
- メンバー
名 の前 に . が付 くのが独特 です。 - メンバーは
常 に pub です。- private や protected や friend はありません。
メソッド
[struct はメソッド( Methods )を
- メソッドの
例 const stdout = @import("std").io.getStdOut().writer(); const sqrt = @import("std").math.sqrt; const Complex = struct { real: f64, imag: f64, pub fn init(real: f64, imag: f64) Complex { return Complex{ .real = real, .imag = imag, }; } pub fn abs(self: Complex) f64 { return sqrt(self.real * self.real + self.imag * self.imag); } }; pub fn main() !void { const cplx = Complex.init(3.3, 4.4); try stdout.print("cplx = {}\n", .{cplx}); try stdout.print("cplx.abs() = {}\n", .{cplx.abs()}); try stdout.print("Complex.abs(cplx) = {}\n", .{Complex.abs(cplx)}); }
実行 結果 cplx = Complex{ .real = 3.3e+00, .imag = 4.4e+00 } cplx.abs() = 5.5e+00 Complex.abs(cplx) = 5.5e+00
- インスタンスを
生成 するメソッド init() と絶対 値 を返 すメソッド abs() を定義 しました。 - インスタンスを
生成 するメソッドに init と名付 けるのも、フィールドにアクセスするメソッドの第 一 引数 を self と名付 けるのも言語 仕様 ではなく慣習 ですが、逸脱 する積極 的 な理由 はありません。
[TODO:
匿名 structリテラル
[Zigでは、structリテラルの
enum
[Zigのenum
は
- enum の
定義 const stdout = @import("std").io.getStdOut().writer(); const Colour = enum { red, green, blue, }; const JISC5062 = enum(u4) { black = 0, brown, red, orange, yellow, green, blue, violet, gray, white, const Self = @This(); const len = @typeInfo(Self).Enum.fields.len; pub fn allCases() [Self.len]Self { var result: [Self.len]Self = undefined; var i: u4 = 0; while (i < Self.len) : (i += 1) { result[i] = @as(Self, @enumFromInt(i)); } return result; } }; pub fn main() !void { try stdout.print("@TypeOf(Colour) ⇒ {}\n", .{@TypeOf(Colour)}); const c: Colour = .green; try stdout.print("c ⇒ {}\n", .{c}); try stdout.print("JISC5062.allCases() ⇒ {any}\n", .{JISC5062.allCases()}); for (JISC5062.allCases()) |e| { try stdout.print("{s} ⇒ {}\n", .{ @tagName(e), @intFromEnum(e) }); } }
実行 結果 @TypeOf(Colour) ⇒ type c ⇒ main.Colour.green JISC5062.allCases() ⇒ { main.JISC5062.black, main.JISC5062.brown, main.JISC5062.red, main.JISC5062.orange, main.JISC5062.yellow, main.JISC5062.green, main.JISC5062.blue, main.JISC5062.violet, main.JISC5062.gray, main.JISC5062.white } black ⇒ 0 brown ⇒ 1 red ⇒ 2 orange ⇒ 3 yellow ⇒ 4 green ⇒ 5 blue ⇒ 6 violet ⇒ 7 gray ⇒ 8 white ⇒ 9
- 3-7,9-31
行 目 が enum の宣言 で、それぞれ変数 に保存 しています。 - enum で
宣言 した値 の型 は type です。 - enum は、struct と
同 じくメンバーとは別 に、型 に属 する変数 やメソッドを持 つことが出来 ます。 - JISC5062.allCases() は、JISC5062のメンバー
全 てを含 む配列 を返 しています。 const Self = @This();
は、コンテナー定義 でコンテナーの識別子 を書 く手間 をなくし、コピーアンドペーストを容易 にします。- red, green と blue が Colour と JISC5062 で
重複 していますが、コンテナーは名前 空間 として機能 するので問題 になりません。
コードショーケース
[const std = @import("std"); const mem = std.mem; const testing = std.testing; const expect = testing.expect; test "Enum w/ method" { // Enumを
定義 します。 const Color = enum { red, green, blue, }; // Enumフィールドを宣言 します。 const primaryColor: Color = .red; _ = primaryColor; // Enumメソッドを持 たせます。 const Suit = enum { clubs, spades, diamonds, hearts, const Self = @This(); pub fn isClubs(self: Self) bool { return self == Self.clubs; } }; // Enumメソッドをテストします。 const p = Suit.spades; try expect(!p.isClubs()); } test "Enum w/ switch" { // Enumをスイッチで使用 します。 const Foo = enum { string, number, none, }; const p2 = Foo.number; const what_is_it = switch (p2) { Foo.string => "This is a string", Foo.number => "This is a number", Foo.none => "This is none", }; try expect(mem.orderZ(u8, what_is_it, "This is a number") == .eq); } test "Enum Literal " { // Enumリテラルを使用 します。 const Status = enum { ok, err, }; const status1: Status = .ok; const status2 = Status.err; try expect(status1 != status2); } test "Non-exhaustive enum" { //非 全 網羅 Enumを作成 します。 const Shape = enum(u8) { circle, square, rectangle, _, }; const shape = Shape.circle; const shapeMessage = switch (shape) { Shape.circle => "This is a circle", Shape.square => "This is a square", Shape.rectangle => "This is a rectangle", _ => "Unknown shape", //非 全 網羅 のため、_ で処理 }; try expect(mem.orderZ(u8, shapeMessage, "This is a circle") == .eq); }
union
[Zigのunion
は、
非 アクティブフィールドを参照 するとコンパイルエラーになりますconst std = @import("std"); const stdout = std.io.getStdOut().writer(); const SimpleUnion = union { char: u8, int: i64, float: f64, }; pub fn main() !void { var su = SimpleUnion{ .char = 'C' }; try stdout.print("su.int = {}\n", .{su.int}); }
- コンパイル
結果 panic: access of inactive union field
- アクティブフィールドであれば
参照 できます const std = @import("std"); const stdout = std.io.getStdOut().writer(); const SimpleUnion = union { char: u8, int: i64, float: f64, }; pub fn main() !void { var su = SimpleUnion{ .char = 'C' }; try stdout.print("su.char = {c}\n", .{su.char}); su = SimpleUnion{ .int = 42 }; try stdout.print("su.int = {}\n", .{su.int}); su = SimpleUnion{ .float = 2.71828_18284_59045_23536_02874_71352 }; try stdout.print("su.float = {}\n", .{su.float}); }
実行 結果 su.char = C su.int = 42 su.float = 2.718281828459045e+00
タグ付 きunion
[union はenumタグタイプを
- タグ
付 きunion const std = @import("std"); const stdout = std.io.getStdOut().writer(); const Tag = enum { char, int, float, }; const TaggedUnion = union(Tag) { char: u8, int: i64, float: f64, }; pub fn main() !void { var tu = TaggedUnion{ .char = 'C' }; try stdout.print("@as(Tag, tu) ⇒ {}\n", .{@as(Tag, tu)}); switch (tu) { Tag.char => |ch| try stdout.print("ch = {c}\n", .{ch}), Tag.int => |i| try stdout.print("i = {}\n", .{i}), Tag.float => |f| try stdout.print("f = {}\n", .{f}), } }
実行 結果 @as(Tag, tu) ⇒ Tag.char ch = C
- タグの enum と、それを
使 うタグ付 きunionのメンバー集合 は一致 していないとエラーになります。 - タグ
付 きunionを式 とするswitch式 では、バターンでタグのメンバーを網羅 している必要 があります(網羅 性 の検証 が行 えます。ただし _ や else をパターン使 うと台無 し)。 匿名 enum版 タグ付 きunionconst std = @import("std"); const stdout = std.io.getStdOut().writer(); const TaggedUnion = union(enum) { char: u8, int: i64, float: f64, }; pub fn main() !void { var tu = TaggedUnion{ .char = 'C' }; try stdout.print("@tagName(tu) ⇒ {s}\n", .{@tagName(tu)}); switch (tu) { .char => |ch| try stdout.print("ch = {c}\n", .{ch}), .int => |i| try stdout.print("i = {}\n", .{i}), .float => |f| try stdout.print("f = {}\n", .{f}), } }
匿名 enum版 タグ付 きunionは、enum のメンバーと union のメンバーの名前 を一致 させる手間 が不要 です。他方 、(unionのメンバーでなく)enum のメンバーに値 を与 えたいときは、実体 化 したタグをつか合 う必要 があります。
switch式 でタグ付 きunionの値 の変更
[switch
- switch
式 でタグ付 きunionの値 の変更 const std = @import("std"); const stdout = std.io.getStdOut().writer(); const Tag = enum { char, int, float, }; const TaggedUnion = union(Tag) { char: u8, int: i64, float: f64, }; pub fn main() !void { var tu = TaggedUnion{ .int = 42 }; try stdout.print("@as(Tag, tu) ⇒ {}\n", .{@as(Tag, tu)}); switch (tu) { Tag.char => |*ch| ch.* -= 1, Tag.int => |*i| i.* += 10, Tag.float => |*f| f.* /= 1.2, } try stdout.print("tu.int ⇒ {}\n", .{tu.int}); }
実行 結果 @as(Tag, tu) ⇒ Tag.int tu.int ⇒ 52
unionのメソッド
[union も
- unionのメソッド
const std = @import("std"); const stdout = std.io.getStdOut().writer(); const signbit = std.math.signbit; const Number = union(enum) { Int: i32, Float: f32, const Self = @This(); //
共用 体 にメソッドを追加 pub fn isPositive(self: Self) bool { return switch (self) { .Int => |value| value > 0, .Float => |value| !signbit(value), }; } }; pub fn main() !void { const i = Number{ .Int = 42 }; const f = Number{ .Float = -4.82 }; try stdout.print("i.isPositive() = {}\n", .{i.isPositive()}); try stdout.print("f.isPositive() = {}\n", .{f.isPositive()}); }実行 結果 i.isPositive() = true f.isPositive() = false
このコードは、ZigのUnionにメソッドをstd.math.signbit
を
まず、Number
というUnionがInt
フィールドと Float
フィールドをconst Self = @This()
を
isPositive
メソッドがstd.math.signbit
を
main
isPositive
メソッドを
コードショーケース
[const std = @import("std"); const testing = std.testing; const expect = testing.expect; // Bare Unionの
定義 const Payload = union { int: i64, float: f64, boolean: bool, }; // Bare Unionの使用 例 test "Bare Union" { var payload = Payload{ .int = 1234 }; try expect(payload.int == 1234); } // Tagged Unionの定義 const ComplexTypeTag = enum { ok, not_ok, }; const ComplexType = union(ComplexTypeTag) { ok: u8, not_ok: void, }; // Tagged Unionの使用 例 test "Tagged Union" { const c = ComplexType{ .ok = 42 }; try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok); try switch (c) { ComplexTypeTag.ok => |value| expect(value == 42), ComplexTypeTag.not_ok => unreachable, }; } // Unionにメソッドを追加 する const Variant = union(enum) { int: i32, boolean: bool, none, fn truthy(self: Variant) bool { return switch (self) { Variant.int => |x_int| x_int != 0, Variant.boolean => |x_bool| x_bool, Variant.none => false, }; } }; // Unionのメソッドの使用 例 test "Union Method" { var v1 = Variant{ .int = 1 }; var v2 = Variant{ .boolean = false }; try expect(v1.truthy()); try expect(!v2.truthy()); } // @tagNameを使用 したEnumの値 の表示 const Small2 = union(enum) { a: i32, b: bool, c: u8, }; // @tagNameの使用 例 test "@tagName" { try expect(std.mem.eql(u8, @tagName(Small2.a), "a")); } //匿名 Unionリテラルの初期 化 const Number = union { int: i32, float: f64, }; //匿名 Unionリテラルの使用 例 test "Anonymous Union Literal Syntax" { const i: Number = .{ .int = 42 }; const f = makeNumber(); try expect(i.int == 42); try expect(f.float == 12.34); } fn makeNumber() Number { return .{ .float = 12.34 }; }実行 結果 1/5 test.Bare Union... OK 2/5 test.Tagged Union... OK 3/5 test.Union Method... OK 4/5 test.@tagName... OK 5/5 test.Anonymous Union Literal Syntax... OK All 5 tests passed.
opaque
[Zigのopaque
は、サイズとアライメントが
これは、
const Derp = opaque {}; const Wat = opaque {}; extern fn bar(d: *Derp) void; fn foo(w: *Wat) callconv(.C) void { bar(w); } test "call foo" { foo(undefined); }
opaque
をfoo
bar
ブロック
[ブロック(Blocks)は、プログラムbreak
構文 (EBNF)block = "{" statement* "}" statement = "comptime"? var-decl | "comptime" block-expr-statement | "nosuspend" block-expr-statement | "suspend" block-expr-statement | "defer" block-expr-statement | "errdefer" pay-load? block-expr-statement | if-statement | labeled-statement | switch-expr | AssignExpr ";"
- [21]
ブロックの値
[ブロックは
const stdout = @import("std").io.getStdOut().writer(); pub fn main() !void { var y: i32 = 123; const x = blk: { y += 1; break :blk y + 2; }; try stdout.print("x = {}, y = {}\n", .{x, y}); }
実行 結果 x = 126, y = 124
- ラベル blk は、
任意 の識別子 に置換 えられますが、慣習 として blk がよく用 いられます。
シャドーイング
[このため、Zigのコードを
defer
[defer は、スコープを
const std = @import("std"); const stdout = std.io.getStdOut().writer(); fn basic_example() !usize { var x: usize = 0; { defer x = 123; x = 0; try stdout.print("@small block: x = {}\n", .{x}); } try stdout.print("@function block: x = {}\n", .{x}); x = 5; return x; } fn multiple_example() !void { try stdout.writeAll("multiple_example(): "); defer { try stdout.writeAll("1 "); } defer { try stdout.writeAll("2 "); } if (false) { // defer
自身 が一 度 も実行 されない場合 は、実行 されません。 defer { try stdout.writeAll("3 "); } } } pub fn main() !void { try stdout.print("basic_example() = {}\n", .{basic_example()}); multiple_example(); }実行 結果 @small block: x = 0 @function block: x = 123 basic_example() = 5 multiple_example(): 2 1
errdefer
[errdefer は、エラーが
errdefer |err| { std.debug.print("the error is {s}\n", .{@errorName(err)}); }
キャスト
[Zigでは、キャスティングは
型 の強制 変換 (Explicit Casts):- Zigでは、
@bitCast
、@alignCast
、@enumFromInt
などのビルトイン関数 を使用 して、明示 的 なキャストを実行 します。 - これらのキャストは、
安全 なものとそうでないものがあります。一部 のキャストはランタイムでの動作 を変更 せず、他 のキャストは言語 レベルのアサーションを実行 します。 - たとえば、
@intCast
は整数 型 間 での変換 を行 いますが、ビットの丸 めは行 いません。これに対 して、@floatCast
は浮動 小数点 数 をより小 さいサイズの浮動 小数点 数 に変換 しますが、精度 が失 われる可能 性 があります。
- Zigでは、
型 の解決 (Peer Type Resolution):switch
、if
、while
、for
などの文脈 で複数 の型 が存在 する場合 、Zigはそれらの型 を解決 します。これにより、すべての型 が変換 可能 な型 が選択 されます。- たとえば、
switch
文 内 でi8
とi16
の変数 を追加 すると、その結果 の型 はi16
になります。これにより、型 が一貫 性 を持 ち、予測 可能 な動作 が実現 されます。
これらのキャスト
- Type Coercion (
型 変換 ):Type Coercion
は、一 つの型 が期待 される場面 で、異 なる型 が提供 された場合 に発生 します。test_type_coercion.zig
では、変数 宣言 や関数 呼 び出 し時 に型 変換 が示 されています。
- Stricter Qualification (
厳格 な修飾 ):同 じランタイム表現 を持 つ値 を、修飾 子 の厳密 さを増 やすためにキャストすることができます。
- Integer and Float Widening (
整数 および浮動 小数点 の拡張 ):整数 および浮動 小数点 の型 が、それを表現 できるより大 きな型 に自動的 に変換 されます。
- Float to Int (
浮動 小数点 から整数 への変換 ):浮動 小数点 数 を整数 にキャストすることは曖昧 であり、コンパイラエラーが発生 します。
- Slices, Arrays and Pointers (スライス、
配列 、ポインタの変換 ):- スライス、
配列 、ポインタの間 での相互 変換 が示 されています。
- スライス、
- Optionals (オプショナル):
- オプショナル
型 のペイロードやnull値 が、オプショナル型 自体 にキャストされることが示 されています。
- オプショナル
- Error Unions (エラーユニオン):
- エラーコードやエラーセットがエラーユニオン
型 にキャストされることが示 されています。
- エラーコードやエラーセットがエラーユニオン
- Compile-Time Known Numbers (コンパイル
時 に既知 の数値 ):数値 が宛先 型 で表現 可能 である場合 にのみ、キャストが行 われることが示 されています。
- Unions and Enums (ユニオンと
列挙 型 ):- タグ
付 きユニオンが列挙 型 に、そして列挙 型 がタグ付 きユニオンにキャストされることが示 されています。
- タグ
- Tuples to Arrays (タプルから
配列 への変換 ):同 じ型 のフィールドを持 つタプルは、配列 にキャストすることができます。
- Explicit Casts (
明示 的 なキャスト):@bitCast
、@alignCast
、@enumFromInt
などのビルトイン関数 を使用 して明示 的 なキャストが行 われます。
- Peer Type Resolution (ピア
型 解決 ):Peer Type Resolution
は、スイッチ式 やif式 などの複数 のオペランド型 が与 えられた場合 に使用 されます。
これらの
型 強制
[Zigにおける
変数 の代入 :変数 がある型 で宣言 されている場合 、その変数 に代入 される値 が宣言 された型 と異 なる場合 、Zigは型 強制 を行 います。ただし、この変換 は安全 な場合 に限 ります。たとえば、整数 型 から浮動 小数点 数 型 への変換 は安全 ですが、逆 の変換 は損失 が発生 する可能 性 があるため、コンパイラは警告 やエラーを生成 することがあります。関数 呼 び出 し時 の引数 の型 の一致 :関数 が特定 の型 の引数 を受 け取 る場合 、関数 呼 び出 し時 に与 えられた引数 の型 が完全 に一致 しない場合 、Zigは適切 な型 に変換 します。これにより、型 の不一致 によるエラーが回避 されます。演算 子 の使用 :演算 子 を使用 する際 に、オペランドの型 が一致 しない場合 、Zigは適切 な型 に変換 して演算 を行 います。たとえば、整数 型 と浮動 小数点 数 型 の演算 を行 う場合 、整数 型 は浮動 小数点 数 型 に変換 されます。明示 的 な型 変換 : プログラマが明示 的 に型 を変換 したい場合 、@as
や@intCast
などの組 み込 み関数 を使用 して、型 変換 を行 うことができます。これにより、プログラマが意図 的 に型 変換 を行 うことができます。
Zigの
明示 的 キャスト
[@bitCast
: ビットレベルでの変換 を行 い、型 のビット表現 を維持 します。このキャストは非常 に低 レベルであり、注意 が必要 です。@alignCast
: ポインタのアライメントを増 やします。より厳格 なアライメントが必要 な場合 に使用 されます。@enumFromInt
:整数 値 から列挙 型 の値 を取得 します。列挙 型 の値 を整数 値 に変換 する逆 の操作 は、自動的 な型 強制 で行 われます。@errorFromInt
:整数 値 からエラーコードを取得 します。@errorCast
: より小 さなエラーセットに変換 します。@floatCast
: より大 きな浮動 小数点 数 型 から小 さな浮動 小数点 数 型 に変換 します。@floatFromInt
:整数 値 から浮動 小数点 数値 に変換 します。@intCast
:整数 型 間 での変換 を行 います。@intFromBool
:真偽 値 を整数 値 に変換 します。@intFromEnum
:列挙 型 のタグ値 を整数 値 として取得 します。@intFromError
: エラーコードを整数 値 として取得 します。@intFromFloat
:浮動 小数点 数値 の整数 部分 を取得 します。@intFromPtr
: ポインタのアドレスを整数 値 として取得 します。@ptrFromInt
:整数 値 をポインタのアドレスに変換 します。@ptrCast
: ポインタ型 間 の変換 を行 います。@truncate
:整数 型 間 での変換 を行 い、ビットを切 り捨 てます。
これらの
ピア型 解決
[Zigのピア
- switch
式 - if
式 - while
式 - for
式 - ブロック
内 の複数 の break文 一部 のバイナリ演算
ピア
整数 の場合 、最大 のビット幅 を持 つ整数 型 が選択 されます。浮動 小数点 数 の場合 、最 も精度 の高 い浮動 小数点 数 型 が選択 されます。- スライスや
配列 などのコンテナ型 の場合 、要素 の型 が共通 の場合 に選択 されます。 - オプショナル
型 の場合 、共通 の基底 型 が選択 されます。 - ポインタ
型 の場合 、共通 のポインタ型 が選択 されます。
これにより、Zigのコンパイラは、
ゼロビット型
[ゼロビット
void
整数 のu0とi0。- len == 0 または 0 ビット
型 の要素 型 を持 つ配列 とベクトル。 - タグが1つしかないenum。
- すべてのフィールドがゼロビット
型 であるstruct。 - ゼロビット
型 のフィールドを1つだけ持 つunion。
Zigでは、ゼロビット
アセンブリ言語 との連携
[[TODO:
atomic
[非同期 関数
[Zigでは、キーワード async を
- async.zig
const std = @import("std"); var frame: anyframe = undefined; pub fn main() !void { try println("begin main"); _ = async func(); try println("resume func"); resume frame; try println("end main"); } fn func() !void { try println("begin func"); frame = @frame(); suspend {} try println("end func"); } fn println(s: []const u8) !void { try std.io.getStdOut().writer().print("{s}\n",.{s}); }
実行 結果 begin main begin func resume func end func end main
unreachable
[Zigのunreachable
は、
if (false) { unreachable; }
Debug と ReleaseSafe モード、および zig test を
ReleaseFast モードでは、オプティマイザーは
コンパイル時
[unreachableの
noreturn型
[noreturnは、つぎ
- break
- continue
- return
- unreachable
- while (true) {}
if
組込 み関数
[comptime
キーワードは、そのパラメーターがコンパイル
@addrSpaceCast(ptr: anytype) anytype
|
キャスト | ポインターを1つのアドレス |
---|---|---|
@addWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
|
a + b を | |
@alignCast(ptr: anytype) anytype
|
アライメント | ptr は *T , ?*T , または []T のいずれかです。ポインターのアライメントを |
@alignOf(comptime T: type) comptime_int
|
アライメント | この |
@as(comptime T: type, expression) T
|
キャスト | |
@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: AtomicOrder) T
|
ポインターをアトミックにデリファレンスしてそのT は、ポインター、bool、 | |
@atomicRmw(comptime T: type, ptr: *T, comptime op: AtomicRmwOp, operand: T, comptime ordering: AtomicOrder) T
|
メモリーをアトミックにT は、ポインター、bool、 | |
@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: AtomicOrder) void
|
T は、ポインター、bool、 | |
@bitCast(value: anytype) anytype
|
キャスト | 1つの |
@bitOffsetOf(comptime T: type, comptime field_name: []const u8) comptime_int
|
オフセット | フィールドのビットオフセットを、それを |
@bitSizeOf(comptime T: type) comptime_int
|
||
@breakpoint()
|
デバッグ | プラットフォーム |
@mulAdd(comptime T: type, a: T, b: T, c: T) T
|
||
@byteSwap(operand: anytype) T
|
キャスト | オペランドのバイト |
@bitReverse(integer: anytype) T
|
キャスト | |
@offsetOf(comptime T: type, comptime field_name: []const u8) comptime_int
|
オフセット | フィールドのバイトオフセットを、それを |
@call(modifier: std.builtin.CallModifier, function: anytype, args: anytype) anytype
|
||
@cDefine(comptime name: []const u8, value) void
|
マクロ |
Cマクロを |
@cImport(expression) type
|
C インタフェース | この
|
@cInclude(comptime path: []const u8) void
|
C インタフェース | この |
@clz(operand: anytype) anytype
|
キャスト | |
@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
|
||
@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
|
||
@compileError(comptime msg: []const u8) noreturn
|
コンパイル |
|
@compileLog(args: ...) void
|
コンパイルログ | |
@constCast(value: anytype) DestType
|
キャスト | ポインターからconst |
@ctz(operand: anytype) anytype
|
キャスト | |
@cUndef(comptime name: []const u8) void
|
マクロ |
Cマクロを |
@cVaArg(operand: *std.builtin.VaList, comptime T: type) T
|
Cマクロva_argを | |
@cVaCopy(src: *std.builtin.VaList) std.builtin.VaList
|
Cマクロva_copyを | |
@cVaEnd(src: *std.builtin.VaList) void
|
Cマクロva_endを | |
@cVaStart() std.builtin.VaList
|
Cマクロva_startを | |
@divExact(numerator: T, denominator: T) T
|
||
@divFloor(numerator: T, denominator: T) T
|
||
@divTrunc(numerator: T, denominator: T) T
|
||
@embedFile(comptime path: []const u8) *const [N:0]u8
|
ファイル |
ファイルの |
@enumFromInt(integer: anytype) anytype
|
キャスト | |
@errorFromInt(value: std.meta.Int(.unsigned, @bitSizeOf(anyerror))) anyerror
|
キャスト | エラーの |
@errorName(err: anyerror) [:0]const u8
|
エラーの | |
@errorReturnTrace() ?*builtin.StackTrace
|
エラー |
エラーの |
@errorCast(value: anytype) anytype
|
キャスト | エラーセットまたはエラーユニオンの |
@export(declaration, comptime options: std.builtin.ExportOptions) void
|
シンボル |
|
@extern(T: type, comptime options: std.builtin.ExternOptions) T
|
||
@fence(order: AtomicOrder) void
|
||
@field(lhs: anytype, comptime field_name: []const u8) (field)
|
フィールドアクセス | コンパイル |
@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: *T) *ParentType
|
ポインター |
フィールドへのポインターから、 |
@floatCast(value: anytype) anytype
|
キャスト | 1つの |
@floatFromInt(int: anytype) anytype
|
キャスト | |
@frameAddress() usize
|
デバッグ | |
@hasDecl(comptime Container: type, comptime name: []const u8) bool
|
メタ |
コンテナに |
@hasField(comptime Container: type, comptime name: []const u8) bool
|
メタ |
|
@import(comptime path: []const u8) type
|
ビルド |
path に |
@inComptime() bool
|
メタ |
このビルトインがコンパイル |
@intCast(int: anytype) anytype
|
キャスト | |
@intFromBool(value: bool) u1
|
キャスト | true を @as(u1, 1)、false を @as(u1, 0) に |
@intFromEnum(enum_or_tagged_union: anytype) anytype
|
キャスト | |
@intFromError(err: anytype) std.meta.Int(.unsigned, @bitSizeOf(anyerror))
|
キャスト | エラーをエラーの |
@intFromFloat(float: anytype) anytype
|
キャスト | |
@intFromPtr(value: anytype) usize
|
キャスト | ポインターを usize に |
@max(a: T, b: T) T
|
a と b の | |
@memcpy(noalias dest, noalias source) void
|
メモリ |
メモリの |
@memset(dest, elem) void
|
メモリ |
メモリ |
@min(a: T, b: T) T
|
a と b の | |
@wasmMemorySize(index: u32) u32
|
メモリ |
|
@wasmMemoryGrow(index: u32, delta: u32) i32
|
メモリ |
|
@mod(numerator: T, denominator: T) T
|
||
@mulWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
|
オーバーフロー | |
@panic(message: []const u8) noreturn
|
デバッグ | パニックハンドラー |
@popCount(operand: anytype) anytype
|
ビット |
|
@prefetch(ptr: anytype, comptime options: PrefetchOptions) void
|
メモリアクセス | プリフェッチ |
@ptrCast(value: anytype) anytype
|
キャスト | 1つの |
@ptrFromInt(address: usize) anytype
|
キャスト | |
@rem(numerator: T, denominator: T) T
|
||
@returnAddress() usize
|
デバッグ | この |
@select(comptime T: type, pred: @Vector(len, bool), a: @Vector(len, T), b: @Vector(len, T)) @Vector(len, T)
|
pred に | |
@setAlignStack(comptime alignment: u29) void
|
スタックアライメント | |
@setCold(comptime is_cold: bool) void
|
||
@setEvalBranchQuota(comptime new_quota: u32) void
|
コンパイル |
コンパイル |
@setFloatMode(comptime mode: FloatMode) void
|
||
@setRuntimeSafety(comptime safety_on: bool) void
|
ランタイムセーフティ | |
@shlExact(value: T, shift_amt: Log2T) T
|
||
@shlWithOverflow(a: anytype, shift_amt: Log2T) struct { @TypeOf(a), u1 }
|
a << b を | |
@shrExact(value: T, shift_amt: Log2T) T
|
||
@shuffle(comptime E: type, a: @Vector(a_len, E), b: @Vector(b_len, E), comptime mask: @Vector(mask_len, i32)) @Vector(mask_len, E)
|
ベクトル |
マスクに |
@sizeOf(comptime T: type) comptime_int
|
メモリ |
メモリ |
@splat(scalar: anytype) anytype
|
ベクトル |
|
@reduce(comptime op: std.builtin.ReduceOp, value: anytype) E
|
ベクトル |
|
@src() std.builtin.SourceLocation
|
デバッグ | |
@sqrt(value: anytype) @TypeOf(value)
|
||
@sin(value: anytype) @TypeOf(value)
|
||
@cos(value: anytype) @TypeOf(value)
|
||
@tan(value: anytype) @TypeOf(value)
|
||
@exp(value: anytype) @TypeOf(value)
|
||
@exp2(value: anytype) @TypeOf(value)
|
||
@log(value: anytype) @TypeOf(value)
|
||
@log2(value: anytype) @TypeOf(value)
|
||
@log10(value: anytype) @TypeOf(value)
|
||
@abs(value: anytype) anytype
|
||
@floor(value: anytype) @TypeOf(value)
|
||
@ceil(value: anytype) @TypeOf(value)
|
||
@trunc(value: anytype) @TypeOf(value)
|
||
@round(value: anytype) @TypeOf(value)
|
||
@subWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
|
a - b を | |
@tagName(value: anytype) [:0]const u8
|
データ |
|
@This() type
|
メタプログラミング | |
@trap() noreturn
|
デバッグ | プログラムを |
@truncate(integer: anytype) anytype
|
||
@Type(comptime info: std.builtin.Type) type
|
メタプログラミング | |
@typeInfo(comptime T: type) std.builtin.Type
|
メタプログラミング | |
@typeName(T: type) *const [N:0]u8
|
メタプログラミング | |
@TypeOf(...) type
|
メタプログラミング | |
@unionInit(comptime Union: type, comptime active_field_name: []const u8, init_expr) Union
|
メタプログラミング | ユニオンの |
@Vector(len: comptime_int, Element: type) type
|
データ |
ベクトルを |
@volatileCast(value: anytype) DestType
|
データ |
ポインタからvolatile |
@workGroupId(comptime dimension: u32) u32
|
プラットフォーム |
|
@workGroupSize(comptime dimension: u32) u32
|
プラットフォーム |
|
@workItemId(comptime dimension: u32) u32
|
プラットフォーム |
@workGroupSize(dimension) ( |
ビルドモード
[Zigのビルドモードは、プログラムをビルドする
- Debug(デフォルト)
- ReleaseFast
- ReleaseSafe
- ReleaseSmall
それぞれのビルドモードは、
ビルドモードをzig build-exe
コマンドを
$ zig build-exe example.zig -O ReleaseFast
- Debug:
実行 時 のパフォーマンスは遅 い安全 性 チェックが有効 - ビルド
速度 が速 い - バイナリサイズが
大 きい
- ReleaseFast:
実行 時 のパフォーマンスが速 い安全 性 チェックが無効 - ビルド
速度 が遅 い - バイナリサイズが
大 きい 再現 可能 なビルド
- ReleaseSafe:
実行 時 のパフォーマンスは中 程度 安全 性 チェックが有効 - ビルド
速度 が遅 い - バイナリサイズが
大 きい 再現 可能 なビルド
- ReleaseSmall:
実行 時 のパフォーマンスは中 程度 安全 性 チェックが無効 - ビルド
速度 が遅 い - バイナリサイズが
小 さい 再現 可能 なビルド
これらのビルドモードは、プロジェクトのニーズや
メモリー管理
[Zigのメモリー
- メモリー
管理 の責任 : Zig言語 では、プログラマーがメモリー管理 を行 います。つまり、Zigにはランタイムがないため、リアルタイムソフトウェア、オペレーティングシステムカーネル、組 み込 みデバイス、低 遅延 サーバーなど、さまざまな環境 でZigコードがシームレスに動作 します。 - アロケーターの
使用 : Zigでは、C言語 とは異 なり、デフォルトのアロケーターは提供 されません。代 わりに、アロケートが必要 な関数 は Allocator パラメーターを受 け入 れます。データ構造 も同様 に、初期 化 関数 で Allocator パラメーターを受 け入 れます。 - アロケーターの
選択 : どのアロケーターを使用 するかは、さまざまな要因 に依存 します。ライブラリを作成 する場合 やlibcをリンクする場合 、またはメモリー要件 が既知 の場合 など、適切 なアロケーターを選択 するためのフローチャートが提供 されています。 - メモリーの
場所 : Zigでは、異 なる種類 のデータが異 なる場所 に配置 されます。例 えば、関数 内 のvar宣言 はその関数 のスタックフレームに配置 されます。一方 、グローバルレベルや構造 体内 のvar宣言 はグローバルデータセクションに配置 されます。 - アロケーターの
実装 : Zigプログラマーは、Allocator インターフェースを満 たすことで独自 のアロケーターを実装 できます。これには、allocFnとresizeFnを提供 する必要 があります。 再帰 :再帰 は、Zigでモデリングソフトウェアにおいて基本 的 なツールですが、無 制限 のメモリー割 り当 て問題 があります。現在 、再帰 は通常 どおり動作 しますが、将来 のZigバージョンではスタックオーバーフローからの保護 が提供 される予定 です。- ライフタイムと
所有 権 : ポインターが指 すメモリが使用 できなくなった場合 に、そのポインターにアクセスしないようにすることが、Zigプログラマーの責任 です。所有 権 とライフタイムのセマンティクスは、ポインターの所有 者 とメモリーの使用 可能 な期間 を明確 にするために、関数 やデータ構造 のAPIドキュメントで説明 されるべきです。
Zigは、メモリー
メモリー管理 の責任 とアロケーター
[Zig
Zigと
- allocator.zig
const std = @import("std"); const Allocator = std.mem.Allocator; const stdout = std.io.getStdOut().writer(); pub fn main() !void { var buffer: [100]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buffer); const allocator = fba.allocator(); const result = try concat(allocator, "foo", "bar"); try stdout.print("concat(allocator, \"foo\", \"bar\") ⇒ \"{s}\"\n", .{result}); } fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 { const result = try allocator.alloc(u8, a.len + b.len); std.mem.copy(u8, result, a); std.mem.copy(u8, result[a.len..], b); return result; }
実行 結果 concat(allocator, "foo", "bar") ⇒ "foobar"
Zig には std.heap.GeneralPurposeAllocator でインポート
アロケーターの選 び方
[どのアロケーターを
- ライブラリーを
作 っているのですか?この場合 、パラメーターとしてアロケーターを受 け取 り、ライブラリーのユーザーにどのアロケーターを使 うかを委 ねるのがベストです。 - libc をリンクしていますか? この
場合 、少 なくともメインのアロケーターは std.heap.c_allocator が正 しい選択 だと思 われます。 必要 なバイト数 の最大 値 は、コンパイル時 に分 かっている数 で制限 されていますか? この場合 、スレッドセーフが必要 かどうかによって std.heap.FixedBufferAllocator か std.heap.ThreadSafeFixedBufferAllocator を使 ってください。- あなたのプログラムはコマンドラインアプリケーションで、
基本 的 な循環 パターンを持 たずに最初 から最後 まで実行 され(ビデオゲームのメインループやウェブサーバーのリクエストハンドラーなど)、最後 にすべてを一 度 に解放 することに意味 があるようなものでしょうか?このような場合 、このパターンに従 うことをお勧 めします。- cli_allocation.zig
const std = @import("std"); pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); const ptr = try allocator.create(i32); std.debug.print("ptr={*}\n", .{ptr}); }
実行 結果 - この
ptr=i32@7fcdf92dd018
種 のアロケーターを使用 する場合 、手動 で何 かを解放 する必要 はありません。arena.deinit()を呼 び出 すと、すべてが一 度 に解放 されます。
- ビデオゲームのメインループやウェブサーバのリクエストハンドラのような
周期 的 なパターンの一部 でしょうか?例 えば、ビデオゲームのフレームが完全 にレンダリングされた後 や、ウェブサーバーのリクエストが処理 された後 など、サイクルの終 わりにすべてのアロケーションを一 度 に解放 できる場合 、std.heap.ArenaAllocator は素晴 らしい候補 となります。前 の箇条書 きで示 したように、これによってアリーナ全体 を一 度 に解放 することができます。また、メモリの上限 を設定 できる場合 は、std.heap.FixedBufferAllocator を使用 すると、さらに最適 化 できることに注意 しましょう。 - テストを
書 いていて、error.OutOfMemoryが正 しく処理 されることを確認 したいですか?この場合 は std.testing.FailingAllocator を使 ってください。 - テストを
書 いていますか?この場合 は std.testing.allocator を使 ってください。 最後 に、上記 のどれにも当 てはまらない場合 は、汎用 のアロケーターが必要 です。Zigの汎用 アロケーターは、設定 オプションのcomptime構造 体 を受 け取 り、型 を返 す関数 として提供 されている。一般 的 には、メイン関数 に std.heap.GeneralPurposeAllocator をひとつセットアップし、アプリケーションの様々 な部分 にそのアロケーターやサブアロケーターを渡 していくことになる。- アロケーターの
実装 を検討 することもできます。
バイトはどこにあるのか?
[Where are the bytes?
”foo” のような
アロケーターの実装
[Zig プログラマーは Allocator インターフェースを
インスピレーションを
ヒープアロケーションの失敗
[Zig のプログラマは、
その
Linuxなどの
- オーバーコミット
機能 を持 つのは一部 のOSだけである。- Linuxはデフォルトで
有効 になっていますが、設定 可能 です。 - Windowsはオーバーコミットしません。
組込 みシステムはオーバーコミットしません。- ホビー
用 OSはオーバーコミットがあってもなくてもよい。
- Linuxはデフォルトで
- リアルタイムシステムでは、オーバーコミットがないだけでなく、
通常 、アプリケーションごとの最大 メモリ容量 があらかじめ決 められています。 - ライブラリーを
作成 する場合 、コードの再 利用 が主 な目的 の1つです。ライブラリーの作成 において、コードの再 利用 は重要 な目的 の一 つである。 - オーバーコミットが
有効 であることに依存 しているソフトウェアもありますが、その存在 は数 え切 れないほどのユーザー体験 の破壊 の原因 となっています。オーバーコミットを有効 にしたシステム、例 えばLinuxのデフォルト設定 では、メモリーが枯渇 しそうになると、システムがロックして使 えなくなる。このとき、OOM Killer はヒューリスティックに基 づき kill するアプリケーションを選択 します。この非決定 的 な判断 により、重要 なプロセスが強制 終了 されることが多 く、システムを正常 に戻 すことができないことがよくあります。
再帰
[ライフタイムとオーナーシップ
[ポインターを
バグを
C言語 との相互 運用
[ZigはC
ZigはC
C言語 型 プリミティブ
[C の void
C言語 ヘッダーからのインポート
[C言語 翻訳 CLI
[コマンドライン・フラグ
[target と -cflags の使用
[cImportとtranslate-cの比較
[C言語 翻訳 キャッシュ
[翻訳 の失敗
[C言語 マクロ
[C言語 ポインター
[C言語 ライブラリーのエクスポート
[オブジェクトファイルの混在
[キーワード一覧
[align
- ポインターのアライメントを
指定 するために使用 します。 - また、
変数 や関数 の宣言 の後 に使用 して、その変数 や関数 へのポインターのアライメントを指定 することができます。 allowzero
- ポインターの
属性 allowzero は、ポインターのアドレスが 0 であることを許可 します。 and
論理 積 anyframe
関数 フレームへのポインターを保持 する変数 の型 として使用 することができます。anytype
関数 のパラメーターは、型 の代 わりにanytypeで宣言 することができます。型 は関数 が呼出 された場所 で推測 されますasm
- インラインアセンブリ
式 を開始 します。これにより、コンパイル時 に生成 される機械 語 を直接 制御 することが