(Translated by https://www.hiragana.jp/)
Zig - Wikibooks コンテンツにスキップ

Zig

出典しゅってん: フリー教科書きょうかしょ『ウィキブックス(Wikibooks)』

本書ほんしょは、プログラミング言語げんごZigのチュートリアルです。 Zigは、堅牢けんろう最適さいてきかつさい利用りよう可能かのうなソフトウェアを維持いじするための汎用はんようシステムプログラミング言語げんごおよびツールチェインです[1]。 Zigは、アンドリュー・ケリー( Andrew Kelley )によって設計せっけいされ、静的せいてきつよ型付かたつけでかた推論すいろんとジェネリックプログラミングをサポートします。

概要がいよう

[編集へんしゅう]

Zigは、2016ねん2がつ発表はっぴょうされた比較的ひかくてきわかいプログラミング言語げんご[2]、2024ねん4がつ23にち現在げんざい最新さいしんバージョンは 0.12.0 であり、pre-release位置いちづけられています[3]。このため Hello world ですら、バージョンあいだ互換ごかんせいがなくなることもあり、今後こんごもリリースバージョンまでは言語げんご仕様しようやライブラリーおよびツールチェインの仕様しよう変更へんこうされる可能かのうせいがあります。

クイックツアー

[編集へんしゅう]

Zigはメモリ安全あんぜんせい高速こうそくなビルド、そして明快めいかい言語げんご設計せっけい特長とくちょうとするあたらしいプログラミング言語げんごです。

以下いかの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)
制御せいぎょ構造こうぞう
ifelse ifelseぶん使つかって条件じょうけん分岐ぶんきができます。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_filevar から 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のおも特徴とくちょうくわしく説明せつめいします。

  1. 静的せいてき型付かたつけ:
    Zigは静的せいてき型付かたつけを採用さいようしています。かたはコンパイル解決かいけつされ、実行じっこうかたエラーをこすことがありません。また、かた推論すいろんもサポートされています。
  2. がた:
    Zigは、がた採用さいようしています。これは、オブジェクトのコピーが作成さくせいされるため、がたはポインタがたよりも安全あんぜんで、予測よそく可能かのう動作どうさをします。
  3. コンパイルメタプログラミング:
    Zigには、コンパイルメタプログラミングをサポートするためのcomptimeブロックがあります。これにより、コンパイル計算けいさん実行じっこうし、コンパイル情報じょうほう収集しゅうしゅうすることができます。
  4. メモリ管理かんり:
    Zigは、メモリ管理かんりについて堅牢けんろう安全あんぜんなアプローチを採用さいようしています。メモリリークやダングリングポインタなどの問題もんだい回避かいひするために、コンパイルにメモリ安全あんぜんせい検査けんさすることができます。
  5. モジュールシステム:
    Zigには、モジュールシステムがあります。モジュールは、依存いぞん関係かんけい管理かんりするための仕組しくみを提供ていきょうします。
  6. ネイティブコード生成せいせい:
    Zigは、ネイティブコードを生成せいせいするコンパイラを提供ていきょうします。これにより、高速こうそくかつ効率こうりつてきなコードを実行じっこうすることができます。
  7. エラーハンドリング:
    Zigには、エラーハンドリング機能きのうがあります。エラーハンドリングは、C言語げんごのエラーコードや例外れいがい処理しょりのようなアプローチにくらべて、より安全あんぜん予測よそく可能かのう動作どうさをすることができます。

以上いじょうが、Zigのおも特徴とくちょうのいくつかです。これらの特徴とくちょうにより、Zigは高速こうそくかつ安全あんぜんなシステムプログラミングを可能かのうにします。

以下いか個別こべつ要素ようそ言及げんきゅうします。

コンパイルがた言語げんご
ひとつまたは複数ふくすうのソースコードをコンパイルして実行じっこうファイルを、それを実行じっこうします。
静的せいてき型付かたつ
変数へんすう関数かんすうのパラメーター・関数かんすうもどなどのかたはコンパイル検証けんしょうされ、かた安全あんぜんせい担保たんぽされます。
例外れいがいはありません
エラー処理しょりなどのための例外れいがいはありません。この用途ようとには、関数かんすうもどエラーユニオンがたオプショナルがた使つかいます。
演算えんざんオーバーロードはありません
演算えんざん演算えんざん一覧いちらんひょうにあるとおりにしか機能きのうしませんし、かならずそのように機能きのうします。シフト演算えんざんがストリーム入出力にゅうしゅつりょくおこなったりはしません。
関数かんすうオーバーロードはありません
関数かんすうあたえられたパラメーターによってちが関数かんすうされることはありません。ただし、可変かへん引数ひきすう関数かんすう定義ていぎできます。その場合ばあいも、おな関数かんすうされます。
かた推論すいろんおこなわれます
変数へんすう宣言せんげんあたえられた初期しょき必須ひっす)から変数へんすうかた推論すいろんすることでかたアノテーション省略しょうりゃくできます。
ガベージコレクション
ガベージコレクションはありません。
クラスはありませんがメソッドはあります
クラスはありませんが、コンテナー( struct, union, enum )がメソッドつことができます。
コンストラクターはありません
慣習かんしゅうてきに init() と名付なづけられたメソッドがコンテナーのインスタンス使つかわれます。
デストラクターはありません
慣習かんしゅうてきに deinit() と名付なづけられたメソッドがコンテナーのインスタンスの解体かいたい処理しょり使つかわれます。deinit() はスコープでをけるとき自動的じどうてき実行じっこうされませんdefer myObj.deinit(); のようにインスタンスの生成せいせい解体かいたい処理しょり登録とうろくします。
継承けいしょうはありません
タグきunionusingnamespace使つかうと継承けいしょうでしたいこと(=差分さぶんプログラミング)が可能かのうですが、構文こうぶん仕組しくみはことなります。
interface や protocol はありません
JavaGo の interface や Swift の protocol はありませんが、コンパイルにメソッドが定義ていぎされているかテストし、なければエラーにするプログラミングはできます。
名前なまえ空間くうかん役割やくわりはコンテナーがにないます
namespace のようなキーワードはありませんが、ドットシンタックス(コンテナー.識別子しきべつし や コンテナー変数へんすう.識別子しきべつし)でコンテナーが名前なまえ空間くうかん役目やくめたします。また、usingnamespace使つかうとドットシンタックスなしに公開こうかい識別子しきべつしにアクセスできるようになります(ミックスインともえます)。
ジェネリックプログラミング対応たいおうしています
関数かんすう呼出よびだしの引数ひきすうに(コンパイル既知きちの)がたわたすことができるので、ジェネリックプログラミングをおこなうことが出来できます。
ソースファイルは匿名とくめいstruct
コンパイル単位たんいであるソースファイルは暗黙あんもくのうちに匿名とくめいstructで、組込くみこ関数かんすう@import() がかえ識別子しきべつし束縛そくばくすることで名前なまえ空間くうかん構成こうせいします。ex: const std = @import("std")
インスタンスがスコープをけるときに実行じっこうする処理しょり登録とうろくできます
スコープをける理由りゆうによって、 defer と errdefer の2種類しゅるい処理しょり登録とうろくできます。
おおくの制御せいぎょ構造こうぞうしきぶん構文こうぶんちます
if, while, for の3つの制御せいぎょ構文こうぶんは、かえしき構文こうぶんとブロックをぶん構文こうぶん両方りょうほうちます。switchは、かえしき構文こうぶんしかありません。
文末ぶんまつ;(セミコロン)の省略しょうりゃくはできません
最近さいきん新興しんこう言語げんごにしてめずらしく ;省略しょうりゃくできません。
ループ構文こうぶんは else ぶしつことができます
whilefor の2つの反復はんぷく構文こうぶんは、ループを「完走かんそう」したとき実行じっこうする else ぶしつことができます。
キーワードを識別子しきべつしにできます
PL/Iのように無制限むせいげんにではなく @"else" のような形式けいしきでキーワードや数字すうじはじまる識別子しきべつし使つかうことができます。
シャドウイング禁止きんし
外部がいぶスコープで使つかわれている識別子しきべつしおな識別子しきべつし内側うちがわのスコープで使つかうことは出来できません。コンパイルエラーになります。
関数かんすうのパラメーターはイミュータブル
関数かんすうのパラメーターは、 const 宣言せんげんされた変数へんすう(=定数ていすう)とおなじく書換かきかえることは出来できません。

サンプルコード・ギャラリー

[編集へんしゅう]

実際じっさいにZigでかれたのコードをてみましょう。

都市としあいだ大圏たいけん距離きょり

[編集へんしゅう]

Ruby#ユーザー定義ていぎクラス都市としあいだ大圏たいけん距離きょりもとめるメソッドを追加ついかしたれいを、Zig移植いしょくしました。

都市としあいだ大圏たいけん距離きょり
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);
}

このコードは、指定していされた絶対ぜったいパスにあるファイル(この場合ばあいは"/etc/hosts")をひらいて、その内容ないよう標準ひょうじゅん出力しゅつりょくむプログラムです。以下いかに、コードのかく部分ぶぶん機能きのう解説かいせつします。

  1. const std = @import("std");: 標準ひょうじゅんライブラリをインポートします。
  2. const stdout = std.io.getStdOut().writer();: 標準ひょうじゅん出力しゅつりょくのライターを取得しゅとくします。
  3. pub fn main() !void { ... }: プログラムのエントリーポイントとなるmain関数かんすうです。エラーが発生はっせいする可能かのうせいがあるため、!voidかた使用しようされています。
  4. const file = try std.fs.openFileAbsolute("/etc/hosts", .{});: /etc/hostsファイルを絶対ぜったいパスとしてひらきます。tryキーワードは、関数かんすうがエラーをかえ可能かのうせいがあることをしめします。
  5. defer file.close();: ファイルの処理しょり終了しゅうりょうしたのちに、かならずファイルをじるようにします。deferキーワードは、そのスコープが終了しゅうりょうするさい処理しょり実行じっこうすることをしめします。
  6. var reader = std.io.bufferedReader(file.reader());: ファイルからりをおこなうためのBufferedReader作成さくせいします。
  7. const in_stream = reader.reader();: BufferedReaderからりストリームを取得しゅとくします。
  8. const allocator = std.heap.page_allocator;: ページアロケータを使用しようしてメモリをてるためのアロケータを定義ていぎします。
  9. const file_size = try file.getEndPos();: ファイルのサイズを取得しゅとくします。
  10. const contents = try in_stream.readAllAlloc(allocator, file_size);: ファイルの内容ないようり、アロケータを使用しようしてメモリをてます。
  11. defer allocator.free(contents);: メモリを解放かいほうするため、contentsがスコープをけたときかなら実行じっこうされるようにします。
  12. 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()

標準ひょうじゅんライブラリーのソート機能きのう使用しようれい

ソートのれい
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のメソッドにしてもいでしょう(ラムダしきほしい)。

エラトステネスのふるい

[編集へんしゅう]

エラトステネスのふるいを、若干じゃっかん Zig らしくいてみました。

エラトステネスのふるい
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/抽象ちゅうしょうクラスを、Crystalに移植いしょくしましたれい題材だいざいに、Zigにはない抽象ちゅうしょうクラスの代替だいたいのコードれいいてみました。

抽象ちゅうしょうクラス(の代替だいたい
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

チートシート

[編集へんしゅう]
エントリーポイント
エラーユニオンがたかえ可能かのうせい考慮こうりょしない場合ばあい
pub fn main() void {}
エラーユニオンがたかえ可能かのうせい考慮こうりょする場合ばあい
pub fn main() !void {}
コメント
// から行末ゆくすえまでがコメントです。
// /* ... */ スタイルのコメントはありません。
変数へんすう宣言せんげん
イミュータブル
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 ( 条件じょうけんしき ) しき else しき
条件じょうけんしきが false でなければifしきしき
false ならなば しき
ifぶん
if ( 条件じょうけんしき ) {
    // ブロック
}
条件じょうけんしきが false でなければブロックを実行じっこう
elseふしあり
if ( 条件じょうけんしき ) {
    // ブロック1 
} else {
    // ブロック2
}
条件じょうけんしきが false でなければブロック1を実行じっこう
else ならばブロック2を実行じっこう
条件じょうけんしきがオプショナルがた
if ( オプショナルしき ) | 変数へんすう | {
    // ブロック1
} else {
    // ブロック2
}
オプショナルしき
null でなければ変数へんすうにキャプチャーしブロック1を実行じっこう
null ならばブロック2を実行じっこう
条件じょうけんしきがエラーユニオンがた
if ( エラーユニオンしき ) | 変数へんすう | {
    // ブロック1
} else | err | {
    // ブロック2
}
エラーユニオンしき
エラーコードでなければ変数へんすうにキャプチャーしブロック1を実行じっこう
エラーコードならば err にキャプチャーしブロック2を実行じっこう
switch
switch ( しき ) {
     => しき,
    2a, 2b => | 変数へんすう | しき, // 変数へんすう2にキャプチャー
    3a => | *変数へんすう | しき, // 変数へんすう3にポインターをキャプチャー
  elase => しき
}
反復はんぷく
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 = しき,
    .メンバー2 = しき,
    .メンバーn = しき
}
匿名とくめいstructリテラル(タプル)
.{
    しき,
    しき,
    しき
}
enum
enumリテラル
enumがた.メンバー
union
unionリテラル
uniontがた{ .メンバー = しき }
タグきunion
関数かんすう
関数かんすう定義ていぎ

基礎きそへん

[編集へんしゅう]

[TODO:くべき項目こうもくならべてみましたが、たとえば「かた」だけでも網羅もうらてきいていくとコンテンツの分量ぶんりょう爆発ばくはつするのがえているので、過剰かじょうになったらリファレンスへん移動いどうするなどの方法ほうほうで、かくふしはコンパクトさをこころがけたい]

エントリーポイント

[編集へんしゅう]

Zigでは、関数かんすう mainがエントリーポイントです。

nop.zig
pub fn main() void {}

なにもしないプログラムはこのようになりますが、エラー集合しゅうごうがたかえ可能かのうせいがあるときは

pub fn main() !void {
   // エラー集合しゅうごうがたかえ可能かのうせいがある処理しょり
}

もどかたvoid から !voidえます。

コメント

[編集へんしゅう]

Zigのコメントにかんする基本きほんてきなポイントを要約ようやくします。

  1. 3つのコメントのタイプ: Zigでは、通常つうじょうのコメント、ドキュメントコメント、トップレベルのドキュメントコメントの3つのタイプがサポートされています。通常つうじょうのコメントは無視むしされますが、ドキュメントコメントとトップレベルのドキュメントコメントは、パッケージのドキュメントを生成せいせいするためにコンパイラによって使用しようされます。
  2. 通常つうじょうのコメント: 通常つうじょうのコメントは //はじまり、つぎ改行かいぎょう文字もじ(LFバイト)までがコメントとしてあつかわれます。
  3. ドキュメントコメント: ドキュメントコメントは、///はじまります。複数ふくすうのドキュメントコメントが連続れんぞくする場合ばあい、1つのふくすうぎょうのドキュメントコメントとして統合とうごうされます。ドキュメントコメントは、それにつづ要素ようそ文書ぶんしょします。
  4. トップレベルのドキュメントコメント: トップレベルのドキュメントコメントは、//!はじまります。これは、現在げんざいのモジュールを文書ぶんしょします。
  5. ドキュメントコメントの位置いち: ドキュメントコメントは特定とくてい場所ばしょにのみ許可きょかされています。たとえば、しき途中とちゅうドキュメントコメントの直前ちょくぜんにドキュメントコメントがある場合ばあい、コンパイルエラーになります。
  6. 通常つうじょうのコメントとの統合とうごう: 現在げんざい、パッケージのドキュメントを生成せいせいするさいに、通常つうじょうのコメントはドキュメントコメントと統合とうごうされます。

Zigのコメントシステムは、コードの文書ぶんしょ理解りかい容易よういにするための強力きょうりょくなツールです。

通常つうじょうのコメント

[編集へんしゅう]

Zigでは、// から行末ゆくすえまでがコメントです。 C言語げんご/* … */ のスタイルのふくすうぎょうわたるコメントはありません。 これは、コードの各行かくこう文脈ぶんみゃく関係かんけいなくトークンできるようにするためです。

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では、/// からはじまるコメントは特別とくべつなコメントで、Docコメント( Doc comments )とばれます。 Docコメントは特定とくてい場所ばしょにしかゆるされません。しき途中とちゅうDocコメントの直前ちょくぜんなど、予想よそうがい場所ばしょにdocコメントがあると、コンパイルエラーになります。

[TODO:サンプルコードと整形せいけい結果けっか]

トップレベルDocコメント

[編集へんしゅう]

Zigでは、//! からはじまるコメントは特別とくべつなコメントで、トップレベルDocコメント( Top-Level Doc Comments )とばれます。 コンテナレベルのドキュメントのように、直後ちょくごのドキュメントにぞくさないユーザードキュメントに、トップレベルDocコメントを使つかいます。

[TODO:サンプルコードと整形せいけい結果けっか]

DocコメントおよびトップレベルDocコメントは、コンパイルzig build-exe -femit-docs ソースファイル.zigように、-femit-docs をあたえると、 docs/ 以下いかにドキュメントが生成せいせいされます。

かた

[編集へんしゅう]

Zigのかたかんする基本きほんてきなポイントを要約ようやくします。

  1. 整数せいすう: 整数せいすうは、i32、i64などのサイズにおうじたかたあらわされます。算術さんじゅつ演算えんざんやビット演算えんざんなどがサポートされます。
  2. 浮動ふどう小数点しょうすうてんすう: 浮動ふどう小数点しょうすうてんすうは、f32、f64などのサイズにおうじたかたあらわされます。浮動ふどう小数点しょうすうてんすう演算えんざんなどがサポートされます。
  3. 真偽しんぎ: trueとfalseの2つのつブールがたがあります。
  4. オプションがた: 存在そんざいするかどうかをしめすオプションがたがあり、nullで初期しょきされます。
  5. エラー統合とうごう: エラーコードまたは正常せいじょう保持ほじするエラー統合とうごうがたがあります。

かた

  1. プリミティブがた: i32、f64、boolなどの基本きほんてきなデータがたがあります。さらに、ポインターサイズの整数せいすう特定とくていのABIに対応たいおうするかたもあります。
  2. 任意にんいかた: void、anyerror、typeなど、特殊とくしゅ目的もくてきかたがあります。
  3. 文字もじれつリテラルとUnicodeコードポイントリテラル: 文字もじれつリテラルは定数ていすうのポインターであり、UTF-8でエンコードされた文字もじれつあらわします。Unicodeコードポイントリテラルは、UTF-8でエンコードされたUnicodeコードポイントをあらわします。
代入だいにゅう変数へんすう
  1. const: constキーワードを使用しようして、定数ていすう変数へんすうてます。一度いちどてられた変更へんこうできません。
  2. var: varキーワードを使用しようして、変数へんすう宣言せんげんし、初期しょきして変更へんこうできるようにします。
  3. undefined: undefined変数へんすう初期しょきせずにのこすために使用しようされます。これは、不正ふせい意味いみし、バグの兆候ちょうこうです。undefinedは、任意にんいかた変換へんかんできますが、そのはその未定義みていぎであることを検出けんしゅつすることはできません。

Zigのかたのシステムは、明確めいかく柔軟じゅうなんなプログラミングをサポートし、安全あんぜんなコードの作成さくせい促進そくしんします。

[TOD0:整数せいすう浮動ふどう小数点しょうすうてんすうbool文字もじれつunionstructenum配列はいれつベクトルスライスポインター・ゼロビットなかた, 関連かんれんする組込くみこ関数かんすう]

さまざまなとそのかた

[編集へんしゅう]
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までのあいだに、安定あんてい定式ていしきはかられることを期待きたいします。

[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 binary16
f32 float 32ビット浮動ふどう小数点しょうすうてんすう仮数かすう23ビット) IEEE-754-2008 binary32
f64 double 64ビット浮動ふどう小数点しょうすうてんすう仮数かすう52ビット) IEEE-754-2008 binary64
f80 double 80ビット浮動ふどう小数点しょうすうてんすう仮数かすう64ビット) IEEE-754-2008 80ビット拡張かくちょう精度せいど
f128 _Float128 128ビット浮動ふどう小数点しょうすうてんすう仮数かすう112ビット) IEEE-754-2008 binary64
bool bool true または false
anyopaque void かた消去しょうきょされたポインター
void 該当がいとうなし) 0ビットがた
noreturn 該当がいとうなし) break, continue, return, unreachable, and while (true) {}かた
type 該当がいとうなし) かたかた
anyerror 該当がいとうなし) エラーコード
comptime_int 該当がいとうなし) コンパイル既知きちたいしてのみ許可きょかされる整数せいすうリテラルのかた
comptime_float 該当がいとうなし) コンパイル既知きちたいしてのみ許可きょかされる浮動ふどう小数点しょうすうてんリテラルのかた
上記じょうき整数せいすうがたくわえ、任意にんいのビットはば整数せいすう参照さんしょうするには、識別子しきべつしとしてiまたはuにつづけて数字すうじもちいることができます。たとえば、識別子しきべつし i7符号ふごうき7ビット整数せいすう意味いみします。この表現ひょうげん整数せいすうがたゆるされる最大さいだいビットはばは65535です。

プリミティブ

[編集へんしゅう]
プリミティブがた( Primitive Values )[20]
名前なまえ 説明せつめい
truefalse 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]
文字もじれつリテラル
[編集へんしゅう]

文字もじれつリテラルは、ヌル終端しゅうたんバイト配列はいれつへの定数ていすうがた単一たんいつ項目こうもくポインターです。文字もじれつリテラルのかたは、ながさとヌル終端しゅうたんであるという事実じじつ両方りょうほうをコードしているため、スライスとヌル終端しゅうたんポインターの両方りょうほう強制きょうせいすることが可能かのうです。文字もじれつリテラルをさい参照さんしょうすると配列はいれつ変換へんかんされます[22]

Zigにおける文字もじれつのエンコーディングは、事実じじつじょうUTF-8であると仮定かていされています。ZigのソースコードはUTF-8でエンコードされているので、ソースコードの文字もじれつリテラルないあらわれるASCIIバイトは、そのUTF-8の意味いみをZigのプログラムない文字もじれつ内容ないようぎ、コンパイラーがそのバイトを修正しゅうせいすることはありません。ただし、UTF-8以外いがいのバイトを文字もじれつリテラルにむことは可能かのうで、その場合ばあいは \xNN 記法きほう使用しようします[22]

Unicodeコードポイントリテラル
[編集へんしゅう]

Unicodeコードポイントリテラルのかたは comptime_int で整数せいすうリテラルおなじです。すべてのエスケープシーケンスは、文字もじれつリテラルと Unicodeコードポイントリテラルの両方りょうほうにおいて有効ゆうこうです[22]

おおくのプログラミング言語げんごでは、Unicodeコードポイントリテラルは「文字もじリテラル」とばれます。しかし、Unicode仕様しよう最近さいきんのバージョン(Unicode 13.0時点じてん)では、「文字もじ」の正確せいかく技術ぎじゅつてき定義ていぎ存在そんざいしません。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は、Zig言語げんごにおいてコンパイルしき評価ひょうかされることをしめします。この概念がいねんは、ジェネリック、定数ていすうしき評価ひょうか、コンパイル最適さいてき、およびコンパイル静的せいてき解析かいせきにおいて重要じゅうよう役割やくわりたします。

以下いかに、comptimeの詳細しょうさい説明せつめいします:

  1. コンパイルパラメータ (Compile-Time Parameters): Zigでは、関数かんすう構造こうぞうたいかた引数ひきすうcomptime使用しようすることができます。これにより、関数かんすう構造こうぞうたい特定とくていかたたいしてコンパイルうことが保証ほしょうされます。コンパイルかた解決かいけつされるため、特定とくていかたとくしたコードを生成せいせいすることができます。
    fn example(comptime T: type) T {
        // Tの特定とくていかたもとづいた処理しょりおこな
    }
    
  2. コンパイル変数へんすう (Compile-Time Variables): comptime修飾しゅうしょく使用しようして、コンパイル評価ひょうかされることが保証ほしょうされた変数へんすう宣言せんげんすることができます。これらの変数へんすうは、コンパイル静的せいてき解析かいせき使用しようされ、ランタイムでのアクセスは許可きょかされません。
    const comptime MAX_VALUE: usize = 100;
    
  3. コンパイルしき (Compile-Time Expressions): comptimeブロックないしき記述きじゅつすることで、そのしきがコンパイル評価ひょうかされることを保証ほしょうできます。このようなしきは、コンパイルのみに影響えいきょうあたえる必要ひつようがある場合ばあい使用しようされます。たとえば、配列はいれつのサイズや定数ていすう計算けいさん使用しようされます。
    const arraySize = comptime 10;
    const comptime result = someFunction();
    
  4. コンパイル制御せいぎょフロー (Compile-Time Control Flow): if, while, for, switchステートメントなどの制御せいぎょフローは、コンパイル評価ひょうかされることが保証ほしょうされます。これにより、特定とくてい条件じょうけんもとづいてコンパイルことなるコードパスをることができます。
    comptime if (someCondition) {
        // このブロックはコンパイル評価ひょうかされる
    } else {
        // このブロックもコンパイル評価ひょうかされる
    }
    

comptimeの使用しようにより、Zig言語げんごではコンパイル高度こうど静的せいてき解析かいせき実行じっこうされ、パフォーマンスの向上こうじょうやコードの安全あんぜんせい確保かくほされます。

このコードはコンパイルエラーになります。

定数ていすう初期しょき関数かんすうもどだとエラー
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適用てきようなケースですが、Zigではつぎのような解決かいけつ方法ほうほうります。下記かきコードはエラーになりません。

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は、言語げんご仕様しようとツールチェインの両方りょうほうテストをサポートしています。 Zigのテストフレームワークは、テストを実行じっこうし、アサーションを評価ひょうかし、カバレッジレポートを生成せいせいするための機能きのう提供ていきょうします。

以下いかは、Zigのテストにかんする概要がいようです。

  1. テスト宣言せんげん: テストは、testキーワードにつづいて名前なまえ(オプション)とテストの本体ほんたいふくむブロックで構成こうせいされます。これにより、コードの特定とくてい部分ぶぶん関数かんすう期待きたいどおりに動作どうさするかを検証けんしょうできます。
  2. テスト実行じっこう: zig testコマンドを使用しようしてテストを実行じっこうします。このコマンドは、テストようのビルドを作成さくせいし、デフォルトのテストランナーを実行じっこうします。テストランナーは、テスト宣言せんげんつけて実行じっこうし、その結果けっか出力しゅつりょくします。
  3. テスト結果けっか報告ほうこく: テストランナーは、テスト結果けっか標準ひょうじゅんエラーに出力しゅつりょくします。成功せいこうしたテスト、失敗しっぱいしたテスト、スキップされたテストなどの情報じょうほう報告ほうこくされます。これにより、開発かいはつしゃはテストの状態じょうたい把握はあくし、問題もんだいがある場合ばあい修正しゅうせいできます。
  4. テストのスキップ: error.SkipZigTestかえすことでテストをスキップすることができます。また、zig testコマンドに--test-filterオプションを指定していして、特定とくていのテストのみを実行じっこうすることもできます。
  5. テストの自動じどう: テストはコードの一部いちぶとして記述きじゅつされるため、変更へんこうがあるたびに手動しゅどう実行じっこうする必要ひつようがありません。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

このれいでは、Zig言語げんごfdiv-inf-nan.zig というファイルに記述きじゅつされたテストコードがしめされています。このコードは、fdiv 関数かんすうたいする3つのテストケースをふくんでいます。

  • "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ひとしいかどうかを期待きたいします。

テストを実行じっこうすると、2つのテストがパスし、1つのテストが失敗しっぱいします。失敗しっぱいしたテストケースは "fdiv 3" です。このテストは、 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では、変数へんすう名前なまえった連続れんぞくしたメモリー領域りょういきで、かたちます。 変数へんすうは、宣言せんげん必要ひつようです。

  1. 変数へんすう宣言せんげん:
    var キーワードまたは const キーワードを使用しようして変数へんすう宣言せんげんします。const使用しようすることが一般いっぱんてきであり、これによりコードのみやすさが向上こうじょうし、最適さいてき機会きかいえます。
  2. 外部がいぶ変数へんすうへのリンク:
    extern キーワードや @extern 関数かんすう使用しようして、のオブジェクトからエクスポートされた変数へんすうとリンクすることができます。
  3. 識別子しきべつし:
    変数へんすう識別子しきべつしは、アルファベットまたはアンダースコアではじまり、その任意にんいかず英数字えいすうじまたはアンダースコアがつづきます。また、キーワードとの重複じゅうふくゆるされません(エスケープする方法ほうほうはあります)。⇒ 識別子しきべつし
  4. コンテナレベルの変数へんすう:
    コンテナレベルの変数へんすうは、静的せいてきなライフタイムをち、コンテナない宣言せんげんされるため、コンテナが評価ひょうかされると初期しょきされます。これらは、構造こうぞうたい共用きょうようたい列挙れっきょがた、または不透明ふとうめいかた内部ないぶ宣言せんげんすることができます。
  5. 静的せいてきローカル変数へんすう:
    関数かんすうないでコンテナを使用しようすることで、静的せいてきなローカル変数へんすう作成さくせいすることも可能かのうです。
  6. スレッドローカル変数へんすう:
    threadlocal キーワードを使用しようして、スレッドごとにことなる変数へんすうインスタンスを作成さくせいすることができます。
  7. ローカル変数へんすう:
    関数かんすうないcomptime ブロックない使用しようされる変数へんすうは、ローカル変数へんすうばれます。これらは、関数かんすうやブロックのスコープないでのみ有効ゆうこうです。
  8. コンパイル変数へんすう:
    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 となります)。

const 変数へんすう

[編集へんしゅう]

キーワード 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変数へんすう宣言せんげんするときも初期しょき必須ひっすです。またいつでも変更へんこうすることはできます。 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;変数へんすう i12変更へんこうします。
  • try stdout.print("i = {}\n", .{i});print メソッドを使用しようして、変更へんこうi標準ひょうじゅん出力しゅつりょく出力しゅつりょくします。
  • i *= i;変数へんすう i自乗じじょうしてさい代入だいにゅうします。
  • try stdout.print("i = {}\n", .{i});print メソッドを使用しようして、さい代入だいにゅうi標準ひょうじゅん出力しゅつりょく出力しゅつりょくします。

変数へんすうのシャドーイングは禁止きんし

[編集へんしゅう]

変数へんすうは、外部がいぶスコープの変数へんすうシャドーイングすることはゆるされません[26]

外部がいぶスコープの識別子しきべつしをシャドーイングすることはゆるされません
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 がローカルスコープで再度さいど宣言せんげんされたため、エラーが発生はっせいしたことがわかります。Zigでは、外部がいぶスコープの変数へんすうをシャドーイングすることはゆるされていないため、このコードはコンパイルエラーとなります。

識別子しきべつし

[編集へんしゅう]

識別子しきべつし英数字えいすうじかアンダースコアではじまり、英数字えいすうじかアンダースコアがいくつでもつづくことができます[26]。また、キーワードとかさなってはいけません[26]

外部がいぶライブラリーとのリンクなど、これらの要件ようけん適合てきごうしない名前なまえ必要ひつよう場合ばあいは、@""構文こうぶん使用しようすることができます。

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で識別子しきべつしにスペースがふくまれる場合ばあいや、数字すうじはじまる識別子しきべつし使用しようする方法ほうほう、および外部がいぶC関数かんすう識別子しきべつしける方法ほうほうしめしています。

以下いかに、かく部分ぶぶん解説かいせつしめします。

  1. const @"identifier with spaces in it" = 0xff;:スペースがふくまれる識別子しきべつし使用しようして、定数ていすう宣言せんげんしています。Zigでは、ダブルクォートでかこまれた文字もじれつ識別子しきべつしとして使用しようすることができます。
  2. const @"1SmallStep4Man" = 112358;数字すうじはじまる識別子しきべつし使用しようして、定数ていすう宣言せんげんしています。同様どうように、ダブルクォートでかこまれた文字もじれつ識別子しきべつしとして使用しようしています。
  3. const c = @import("std").c;外部がいぶライブラリをインポートして、その一部いちぶc という名前なまえ定数ていすうとして使用しようします。これにより、外部がいぶのCライブラリで定義ていぎされたかた関数かんすうにアクセスすることができます。
  4. pub extern "c" fn @"error"() void;外部がいぶC関数かんすう識別子しきべつしけて宣言せんげんしています。extern "c" はC言語げんご規約きやく指定していし、voidもどかたしめしています。
  5. pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;同様どうように、外部がいぶC関数かんすう識別子しきべつしけて宣言せんげんしています。この関数かんすう引数ひきすうり、c_int かたもどとしてかえします。
  6. const Color = enum { red, @"really red", };列挙れっきょがた Color定義ていぎしています。@"really red" のように、列挙れっきょにも識別子しきべつしけることができます。
  7. const color: Color = .@"really red";Color 列挙れっきょがた変数へんすう color宣言せんげんし、@"really red"列挙れっきょ初期しょきとして指定していしています。

このように、Zigでは識別子しきべつしにスペースや数字すうじふくめることができ、また識別子しきべつし外部がいぶC関数かんすうけることもできます。これにより、ことなる名前なまえ規則きそくつライブラリや関数かんすうをZigのコードから使用しようすることができます。

https://ziglang.org/documentation/master/#Identifiers から引用いんよう

整数せいすう

[編集へんしゅう]

整数せいすうリテラル

[編集へんしゅう]

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進数しんすうはそれぞれ 0b0o0xまえおけします。
おおくの言語げんごことなり 0つぎ文字もじ小文字こもじ必須ひっすで、大文字おおもじけません。

実行じっこう整数せいすう

[編集へんしゅう]

整数せいすうリテラルにはサイズ制限せいげんがなく、不明ふめい動作どうさ発生はっせいする場合ばあいにはコンパイラがそれをキャッチします。

ただし、整数せいすうがコンパイルには不明ふめいである場合ばあい、サイズが判明はんめいしている必要ひつようがあり、未定義みていぎ動作どうさ影響えいきょうけます。

fn divide(a: i32, b: i32) i32 {
    return a / b;
}

このような関数かんすうでは、 ab実行じっこうのみに判明はんめいするため、この除算じょざん操作そうさ整数せいすうオーバーフローやゼロ除算じょざん影響えいきょうける可能かのうせいがあります。

演算えんざん

[編集へんしゅう]

整数せいすう演算えんざんでは、+- などの演算えんざん整数せいすうオーバーフローにたいして定義ていぎ動作どうさこします。わりに、すべてのターゲットにたいしてラッピングおよびサチュレーティング演算えんざんおこなうための代替だいたい演算えんざん提供ていきょうされています。

  • ラッピング演算えんざん+% および -%
  • サチュレーティング演算えんざん+| および -|

Zigでは任意にんいのビットはば整数せいすうもサポートされており、i または uのち数字すうじつづ識別子しきべつし使用しようして参照さんしょうされます。たとえば、i7 は7ビットの符号ふごう整数せいすうしめします。符号ふごう整数せいすうがた場合ばあい、Zigでは2の補数ほすう表現ひょうげん使用しようされます。

ラッピング演算えんざんおよびサチュレーティング演算えんざんとオーバーフローのれい
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 かた整数せいすう範囲はんいえたためです。このような場合ばあい、Zigは整数せいすうオーバーフローを検出けんしゅつしてプログラムをパニックさせます。一方いっぽうで、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に対応たいおう

浮動ふどう小数点しょうすうてんすうリテラル

[編集へんしゅう]

浮動ふどう小数点しょうすうてんすうリテラルは、comptime_floatがたち、最大さいだいほか浮動ふどう小数点しょうすうてんすうがた(f128)とおな精度せいど操作そうさ保証ほしょうします。

浮動ふどう小数点しょうすうてんすうリテラルは、任意にんい浮動ふどう小数点しょうすうてんすうがたおよび整数せいすうがた小数点しょうすうてん以下いかがない場合ばあい)に変換へんかんされます。

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);

浮動ふどう小数点しょうすうてんすう演算えんざん

[編集へんしゅう]

デフォルトでは、浮動ふどう小数点しょうすうてんすう演算えんざんはStrictモードを使用しようしますが、ブロックごとにOptimizedモードにえることができます。

export fn foo_strict(x: f64) f64 {
    return x + big - big;
}

export fn foo_optimized(x: f64) f64 {
    @setFloatMode(.Optimized);
    return x + big - big;
}

上記じょうきれいでは、演算えんざんをStrictモードとOptimizedモードで比較ひかくしています。

演算えんざん

[編集へんしゅう]

Zigの演算えんざん概要がいよう以下いかとおりです:

加算かさん減算げんざん

[編集へんしゅう]
  • 加算かさんa + ba += b
  • 減算げんざんa - ba -= b
  • ラップ加算かさんa +% ba +%= b
  • ラップ減算げんざんa -% ba -%= b
  • 飽和ほうわ加算かさんa +| ba +|= b
  • 飽和ほうわ減算げんざんa -| ba -|= b

乗算じょうざん除算じょざん

[編集へんしゅう]
  • 乗算じょうざんa * ba *= b
  • 除算じょざんa / ba /= b
  • ラップ乗算じょうざんa *% ba *%= b
  • 飽和ほうわ乗算じょうざんa *| ba *|= b
  • 剰余じょうよ除算じょざんa % ba %= b

ビット演算えんざん

[編集へんしゅう]
  • ビットシフト:ひだりシフト a << bみぎシフト a >> b
  • ビット AND:a & ba &= b
  • ビット OR:a | ba |= b
  • ビット XOR:a ^ ba ^= 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 ba catch |err| b

その

[編集へんしゅう]
  • アドレス取得しゅとく&a
  • ポインタの参照さんしょうa.*

演算えんざん詳細しょうさいは、Zigのドキュメントやリファレンスを参照さんしょうしてください。

制御せいぎょ構造こうぞう

[編集へんしゅう]

Zigは、やや関数かんすうがたプログラミング言語げんご影響えいきょうけており、おおくの構文こうぶんちます。 Zigの制御せいぎょ構造こうぞうおおくはしき構文こうぶんぶん構文こうぶんちます(例外れいがい#switchしき構文こうぶんしかありません)。

以下いかKotlin#分岐ぶんきから、一部いちぶれい移植いしょくしました。

分岐ぶんき

[編集へんしゅう]

Zigには、#if#switchふたつの分岐ぶんき構文こうぶんがあります。

Zigでは、if分岐ぶんきする ifしき とブロックを分岐ぶんきする 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を省略しょうりゃくすると
[編集へんしゅう]

ifしきで、条件じょうけん成立せいりつせずelseを省略しょうりゃくされたとき、しきvoid となります。

条件じょうけん不成立ふせいりつで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");
    }
}
コンパイルエラー
./main.zig:7:9: error: expected type 'bool', found 'comptime_int'
    if (i) { 
        ^
Zigでは、if にかぎらず、条件じょうけんしきは、boolオプショナルがたあるいはエラーユニオンがたでなければいけません。
    if (i != 0) {
とします。
オプショナルがた条件じょうけんとしたif
[編集へんしゅう]

ifの条件じょうけんしきにはオプショナルがた( ?T )を使つかうことが出来できます。この場合ばあいは、通常つうじょうのほか null を想定そうていでき、null に出会であった場合ばあいは else ぶし実行じっこうされます。

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初期しょきしています。

つぎに、最初さいしょのifぶんでは、anullでない場合ばあいにブロックない処理しょり実行じっこうされます。この場合ばあい|*value|構文こうぶん使つかってオプショナルがたし、そのポインターをvalueてています。そのした出力しゅつりょくしてから、その123えています。

2番目ばんめのifぶん同様どうように、anullでない場合ばあいにブロックない処理しょり実行じっこうされます。ただし、こちらでは|n|構文こうぶん使つかってオプショナルがたそのものをし、その出力しゅつりょくしています。

最後さいごのifぶんでは、anullてています。そのanullであるかどうかをチェックし、null場合ばあいにはelseブロックない処理しょり実行じっこうされます。ここでは、|_|構文こうぶん使つかって、がないことをしめしています。そのanullであることを出力しゅつりょくしています。

実行じっこう結果けっかると、それぞれのifぶん条件じょうけんおうじてまさしく動作どうさしていることがわかります。

エラーユニオンがた条件じょうけんとしたif
[編集へんしゅう]

ifの条件じょうけんしきにはエラーユニオンがた( !T )を使つかうことが出来できます。この場合ばあいは、通常つうじょうのほかエラーコードを想定そうていでき、エラーコードに出会であった場合ばあいは else |err| ふし実行じっこうされ、err がエラーコードです。

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というエラーコードをてます。

つぎに、最初さいしょのifぶんでは、bがエラーコードをっている場合ばあいにはelse |err|ブロックない処理しょり実行じっこうされます。この場合ばあい|err|構文こうぶん使つかってエラーコードをし、そのエラーコードをデバッグ出力しゅつりょくしています。

2番目ばんめのifぶん同様どうように、bがエラーコードをっている場合ばあいにはelse |_|ブロックない処理しょり実行じっこうされます。ただし、こちらでは|n|構文こうぶん使つかってエラーコードのそのものをし、そのをデバッグ出力しゅつりょくしています。

実行じっこう結果けっかると、それぞれのifぶん条件じょうけんおうじてまさしく動作どうさしていることがわかります。最初さいしょ場合ばあいではエラーコードが出力しゅつりょくされ、2番目ばんめ場合ばあいでは正常せいじょう出力しゅつりょくされています。

Zigでは、switchしきかえします。switchぶんはありません。switch-prong(分岐ぶんきさき)のかた一致いっちしている必要ひつようがあります。

構文こうぶん(EBNF)
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 ]
[21]
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にはelseふしもあり、ループ完了かんりょう実行じっこうされます。また、ラベルをけてネストしたループからのbreakcontinue可能かのうです。オプショナルがたやエラーユニオンがた条件じょうけんけ、それらのをキャプチャできます。インライン可能かのうで、コンパイル最適さいてきかた利用りよう可能かのうです。

構文こうぶん(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をともなったwhileの使つかいどころ
Zigのwhileループは完走かんそうすると実行じっこうする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});
    }
}
フラッグがなくなり簡素かんそになりました。
Zig以外いがい言語げんごでは、Pythonでも else をともなったループ構文こうぶんがあります。
ラベルきwhile
[編集へんしゅう]

whileループにラベルをけると、ネストしたループないからのbreakやcontinueから参照さんしょうできます[28]

ラベルき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ループは条件じょうけんとしてオプショナルがたり、ペイロードをキャプチャすることができます。null に遭遇そうぐうした場合ばあい、ループは終了しゅうりょうします[29]

while しきに |x| 構文こうぶんがある場合ばあい、while 条件じょうけんオプショナルがた(あるいはつぎべるエラーユニオンがた)でなければなりません(この x かキャプチャされたペイロードです)。

オプショナルがた条件じょうけんとした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ループは条件じょうけんとしてエラーユニオンがたり、ペイロードをキャプチャすることができます。エラーコードに遭遇そうぐうした場合ばあい、ループは終了しゅうりょうします[30]

while しきに |x| 構文こうぶんがある場合ばあい、while 条件じょうけんエラーユニオンがた(あるいはまえべたオプショナルがた)でなければなりません(この x かキャプチャされたペイロードです)。

while しきに else |x| 構文こうぶんがある場合ばあい、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ループはインラインすることができる。これにより、ループが展開てんかいされ、コンパイルにしかできないこと、たとえば、かたをファーストクラスのとして使用しようすることなどができるようになります[31]

[TODO:コードれい]

Zigのforループは、スライスや配列はいれつをイテレートするさい柔軟じゅうなんせい提供ていきょうします。要素ようそごとに処理しょりおこない、continuebreak使用しようして制御せいぎょフローを操作そうさできます。インデックスや複数ふくすうのオブジェクトの同時どうじイテレーション、参照さんしょうによるイテレーションなど、多彩たさい機能きのうちます。また、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( Labeled for )とは、ラベルをともなった for ループでラベルも for 構文こうぶん一部いちぶです。for ループにラベルをけると、ネストしたループないからのbreakやcontinueから参照さんしょうできます[32]

ラベルき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

Forループはインラインすることができます。これにより、ループが展開てんかいされ、コンパイルにしかできないこと、たとえば、かたをファーストクラスのとして使用しようすることなどができるようになります。インラインされたforループのキャプチャとイテレータは、コンパイル既知きちです[33]

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の関数かんすう引数ひきすうは、const で宣言せんげんされた変数へんすうおなじくイミュータブルです。 ミュータブルにする方法ほうほうはありません。

構文こうぶん(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ではトップレベルの識別子しきべつし処理しょりけい参照さんしょう解決かいけつ(とシグネチャーをふくめたかた一致いっち確認かくにん)を引受ひきうけてくれます。

  1. 基本きほんてき関数かんすう定義ていぎ: Zigでの関数かんすう定義ていぎは、以下いかのようにおこないます。
    fn add(a: i8, b: i8) i8 {
        return a + b;
    }
    
    このれいでは、addという関数かんすう定義ていぎされており、2つのi8かた引数ひきすうり、i8かたかえします。
  2. 外部がいぶ関数かんすう定義ていぎ: Zigでは、外部がいぶライブラリやAPIから関数かんすう利用りようするために、externキーワードを使用しようして外部がいぶ関数かんすう宣言せんげんします。
    extern "kernel32" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
    
    このれいでは、Windowsのkernel32ライブラリからExitProcessという関数かんすう使用しようしています。
  3. パラメータのかた推論すいろん: 関数かんすうのパラメータのかたは、anytype使用しようして関数かんすうされるときに推論すいろんされます。
    fn addFortyTwo(x: anytype) @TypeOf(x) {
        return x + 42;
    }
    
    このれいでは、addFortyTwo関数かんすうのパラメータxかた推論すいろんされます。
  4. インライン関数かんすう: inlineキーワードを使用しようすることで、関数かんすうをコールサイトにインライン展開てんかいすることができます。
    inline fn foo(a: i32, b: i32) i32 {
        return a + b;
    }
    
    このれいでは、foo関数かんすうがインラインで展開てんかいされ、コンパイル計算けいさんおこなわれます。
  5. 関数かんすうポインタ: 関数かんすうとしてあつかうために、関数かんすうポインタを使用しようすることができます。
    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 )もひとつのかたです[34]

エラー集合しゅうごうがた

[編集へんしゅう]

エラー集合しゅうごうかた( Error Set Type )は、enum のようなものです。おなじエラーめいふくすうかい宣言せんげんすることは可能かのうで、宣言せんげんした場合ばあいおな整数せいすうてられます[35]。 コンパイル全体ぜんたいのユニークなエラーかずが、エラー集合しゅうごうがたのサイズを決定けっていするはずです。しかし、いま[36]は u16 になるようにハードコーディングされています。 サブセットからスーパーセットへエラーを強制きょうせいすることができます。

構文こうぶん(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 は、グローバルエラー集合しゅうごう参照さんしょうします。これは、コンパイルユニット全体ぜんたいのすべてのエラーをふくむエラー集合しゅうごうです。これはのすべてのエラー集合しゅうごうのスーパーセットで、どのエラー集合しゅうごうのサブセットでもありません。

任意にんいのエラー集合しゅうごうをグローバルエラー集合しゅうごう強制きょうせいすることができ、グローバルエラー集合しゅうごうのエラーをグローバルエラー集合しゅうごう明示めいじてきにキャストすることができます。この場合ばあい言語げんごレベルのアサートが挿入そうにゅうされ、エラー宛先あてさきのエラー集合しゅうごう実際じっさいふくまれていることが確認かくにんされます。

グローバルエラー集合しゅうごうは、コンパイラーがコンパイルにどのようなエラーがこりうるかをることができないため、一般いっぱんけるべきです。コンパイルにエラー集合しゅうごうっていたほうが、生成せいせいされるドキュメントや有用ゆうようなエラーメッセージ(たとえば switch でこりうるエラーわすれてしまうなど)に有利ゆうりです。

エラーユニオンがた

[編集へんしゅう]

エラー集合しゅうごうがた正常せいじょうがたこう演算えんざん !結合けつごうして、エラーユニオンがた( Error Union Type )にすることができます。

var error_or_value: AllocationError ! u16 = 10;

エラーユニオンがたは、エラー集合しゅうごうがた単体たんたいよりも頻繁ひんぱん使用しようされる可能かのうせいがあります[37]

構文こうぶん(EBNF)
error-union-expr = suffix-expr ( "!" TypeExpr )?
suffix-expr = "async" PrimaryTypeExpr SuffixOp* FnCallArguments
            |       PrimaryTypeExpr ( SuffixOp | FnCallArguments )*
[21]

演算えんざん

[編集へんしゅう]

Zigには、演算えんざんのオーバーロードはありません。 Zigのプログラムに演算えんざんたとき、それがつぎ一覧いちらんひょうしめすもので、それ以外いがいのものでないことが保証ほしょうされます[38]

演算えんざん一覧いちらんひょう

[編集へんしゅう]
演算えんざん一覧いちらんひょう( Table of Operators )[39]
構文こうぶん 関連かんれんするかた 説明せつめい コードれい
a + b
a += b
加算かさん
整数せいすう場合ばあい、オーバーフローをこす可能かのうせいがあります。
オペランドにたいしてピアがた解決かいけつおこないます。
2 + 5 == 7
a +% b
a +%= b
ラッピング加算かさん
補数ほすうのラップ動作どうさ保証ほしょうされています。
オペランドにたいしてピアがた解決かいけつおこないます。
@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
減算げんざん
整数せいすう場合ばあい、オーバーフローをこす可能かのうせいがあります。
オペランドにたいしてピアがた解決かいけつおこないます。
2 - 5 == -3
a -% b
a -%= b
ラッピング減算げんざん
補数ほすうのラップ動作どうさ保証ほしょうされています。
オペランドにたいしてピアがた解決かいけつおこないます。
@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
乗算じょうざん
整数せいすう場合ばあい、オーバーフローをこす可能かのうせいがあります。
オペランドにたいしてピアがた解決かいけつおこないます。
2 * 5 == 10
a *% b
a *%= b
ラッピング乗算じょうざん
補数ほすうのラップ動作どうさ保証ほしょうされています。
オペランドにたいしてピアがた解決かいけつおこないます。
@as(u8, 200) *% 2 == 144
a *| b
a *|= b
* 整数せいすう
飽和ほうわ乗算じょうざん
オペランドにたいしてピアがた解決かいけつおこないます。
@as(u8, 200) *| 2 == 255
a / b
a /= b
除算じょざん
整数せいすう場合ばあい、オーバーフローをこす可能かのうせいがあります。
整数せいすう場合ばあい、ゼロ除算じょざんこす可能かのうせいがあります。
FloatMode.Optimized モードにいて、浮動ふどう小数点しょうすうてんすうたいしてゼロ除算じょざん発生はっせいすることがあります。
符号ふごう整数せいすうのオペランドは、既知きち( comptime-known )かつせいでなければなりません。その場合ばあいは、@divTrunc@divFloor、または @divExactわりに使用しようしてください。
オペランドにたいしてピアがた解決かいけつおこないます。
10 / 5 == 2
a % b
a %= b
剰余じょうよ演算えんざん
整数せいすう場合ばあい、ゼロ除算じょざんこす可能かのうせいがあります。
FloatMode.Optimized モードにいて、浮動ふどう小数点しょうすうてんすうたいしてゼロ除算じょざん発生はっせいすることがあります。
符号ふごうきまたは浮動ふどう小数点しょうすうてんオペランドは、既知きちせいでなければなりません。それ以外いがい場合ばあいは、わりに @rem または @mod を使用しようしてください。
オペランドにたいしてピアがた解決かいけつおこないます。
10 % 3 == 1
a << b
a <<= b
* 整数せいすう
ひだりビットシフト
b は、既知きちであるか、aのビットすうの log2 をかたでなければなりません。
1 << 8 == 256
a <<| b
a <<|= b
* 整数せいすう
飽和ほうわひだりビットシフト
@as(u8, 1) <<| 8 == 255
a >> b
a >>= b
* 整数せいすう
みぎビットシフト
b は、既知きちであるか、aのビットすうの log2 をかたでなければなりません。
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
もし anullならば、b("デフォルト")をかえす、そうでなければ,ラップされていない aかえす。bnoreturnがたである可能かのうせいがあることに注意ちゅうい
const value: ?u32 = null;
const unwrapped = value orelse 1234;
unwrapped == 1234
a.?
以下いかおな
a orelse unreachable
const value: ?u32 = 5678;
 value.? == 5678
a catch b
a catch |err| b
もし aerrorならば、b("デフォルト")をかえす、そうでなければ,ラップされていない aかえす。code>b がnoreturnがたである可能かのうせいがあることに注意ちゅういerrはエラーであり,しきbのスコープないにある。
const value: anyerror!u32 = error.Broken;
const unwrapped = value catch 1234;
unwrapped == 1234
a and b
論理ろんりせき
もし afalse ばらば b評価ひょうかせずに falseかえす。そうでなければ bかえす。
(false and true) == false
a or b
論理ろんり
もし atrue ばらば b評価ひょうかせずに trueかえす。そうでなければ bかえす。
(false or true) == true
!a
論理ろんり否定ひてい
!false == true
a == b
abひとしい場合ばあいtrue を、そうでない場合ばあいfalseかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(1 == 1) == true
a == null
anull場合ばあいtrue を、そうでない場合ばあいfalseかえします。
const value: ?u32 = null;
value == null
a != b
abひとしい場合ばあいfalse を、そうでない場合ばあいtrueかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(1 != 1) == false
a > b
ab よりおおきい場合ばあいtrue を、そうでない場合ばあいfalseかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(2 > 1) == true
a >= b
ab よりおおきいあるいはひとしい場合ばあいtrue を、そうでない場合ばあいfalseかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(2 >= 1) == true
a < b
ab よりちいさい場合ばあいtrue を、そうでない場合ばあいfalseかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(1 < 2) == true
>
a <= b
ab よりちいさいあるいはひとしい場合ばあいtrue を、そうでない場合ばあいfalseかえします。オペランドにたいしてピアがた解決かいけつおこないます。
(1 <= 2) == true
a ++ b
配列はいれつ結合けつごう
ab既知きち場合ばあいのみ使用しよう可能かのうです。
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
配列はいれつ乗算じょうざん
ab既知きち場合ばあいのみ使用しよう可能かのうです。
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では、四則しそく演算えんざんなどの演算えんざんはディフォルトでオーバーフローを検出けんしゅつします。それとはべつに、(C言語げんごなどのように)ラッピングをおこな演算えんざんべつ用意よういされています。

オーバーフローのれい
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には、四則しそく演算えんざんなどの演算えんざん飽和ほうわ演算えんざん( Saturation calculation )バージョンが用意よういされています。

飽和ほうわ演算えんざんれい
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には「オプショナルがた」( optional type )と「オプショナルがた」があります。

オプショナルがた
として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 ** aryary ++ ary ++ ary等価とうかです。

多次元たじげん配列はいれつ

[編集へんしゅう]

多次元たじげん配列はいれつ( Multidimensional Arrays )は、ネストした配列はいれつ生成せいせいします[40]

多次元たじげん配列はいれつ
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ループで初期しょきしています。

センチネル終端しゅうたん配列はいれつ

[編集へんしゅう]

センチネル終端しゅうたん配列はいれつ( Sentinel Terminated Arrays )は、文字もじれつのように特殊とくしゅあたい(センチネル( sentinel番兵ばんぺい ))で終端しゅうたんした配列はいれつです。 文字もじれつ場合ばあいのセンチネルは ’\0’ ですが、構文こうぶん [配列はいれつちょう:x]要素ようそがた の x で任意にんいをセンチネルにできます[41]

センチネル終端しゅうたん配列はいれつ
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整数せいすう浮動ふどう小数点しょうすうてんすうポインターのグループで、可能かのうであればSIMD命令めいれい使つかって並列へいれつ操作そうさされます。ベクトルがたは、組込くみこ関数かんすう @Vector作成さくせいします[42]

ベクトルは、基本きほんがたおな演算えんざんをサポートしています。これらの演算えんざん要素ようそごとにおこなわれ、入力にゅうりょくベクトルとおなながさのベクトルをかえします。これには以下いかふくまれます。

算術さんじゅつ演算えんざん
+, -, /, *, @divFloor, @sqrt, @ceil, @log, など
ビット演算えんざん
>>, <<, &, |, ~, など
比較ひかく演算えんざん
<, >, ==, など

スカラー(個々ここ数値すうち)とベクトルが混在こんざいしている場合ばあい数学すうがく演算えんざん使用しようすることは禁止きんしされています。Zigでは、スカラーからベクトルへの変換へんかん容易よういにするために組込くみこ関数かんすう@splat用意よういされており、ベクトルからスカラーへの変換へんかんには組込くみこ関数かんすう@reduce配列はいれつインデックスの構文こうぶんがサポートされています。ベクトルは、ながさが既知きち固定こていちょう配列はいれつとのあいだ代入だいにゅうもサポートしています。

Zigは,組込くみこ関数かんすう@shuffle組込くみこ関数かんすう@select提供ていきょうし,ベクトルないやベクトルあいだ要素ようそならえをおこないます.

ターゲットマシンのネイティブ SIMD サイズよりみじかいベクトルにたいする操作そうさは、通常つうじょう単一たんいつの SIMD 命令めいれいにコンパイルされます。ある演算えんざんがターゲット・アーキテクチャで SIMD をサポートしていない場合ばあい、コンパイラーはデフォルトでかくベクトル要素ようそたいしていちに 1 つずつ演算えんざんします。Zig は、232-1 までの既知きちのベクトルちょうをサポートしていますが、2 の累乗るいじょう(2-64)がもっと一般いっぱんてきです。ただし、現在げんざいの Zig では、ながすぎるベクトルちょう (たとえば 220) はコンパイラーがクラッシュする可能かのうせいがあります。

ベクトル演算えんざん
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には、単一たんいつ項目こうもく多項目たこうもくの2種類しゅるいのポインターがあります[43]

  • *T - 正確せいかくに1つの項目こうもくへの単一たんいつ項目こうもくポインター。
    • deref構文こうぶんをサポート: ptr.*
  • [*]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

単一たんいつ項目こうもくポインター

[編集へんしゅう]

たん項目こうもくポインターをるには、&xを使用しようします。

多項目たこうもくポインター

[編集へんしゅう]

[TODO]

ロードとストアは、副作用ふくさようがないことが前提ぜんていです。MMIO (Memory Mapped Input/Output) のように、ロードやストアが副作用ふくさようつべき場合ばあいは volatile を使用しようします[44]

アライメント

[編集へんしゅう]

それぞれのかたにはアライメント( Alignment )があり、そのかたがメモリーからロードされたり、メモリーにストアされたりするとき、メモリーアドレスがこのかず均等きんとうれるようなバイトすうになっています。この組込くみこ関数かんすう @alignOf使つかってることができます[45]

アラインメントはCPUアーキテクチャに依存いぞんしますが、つねに2のべきじょうであり、1 << 29 よりちいさいです。

Zigでは、ポインターかたはアライメントっています。この基礎きそとなるかたのアライメントとひとしい場合ばあい、そのかた省略しょうりゃくすることができます。

align は、ポインターのアライメントを指定していするために使用しようします。また、変数へんすう関数かんすう宣言せんげんのち使用しようして、その変数へんすう関数かんすうへのポインターのアライメントを指定していすることができます。

allowzeroポインター属性ぞくせいは、ポインターのアドレスがゼロであることを許可きょかします。これは、アドレスゼロがマッピング可能かのう独立どくりつがたOSターゲットでのみ必要ひつようとされます。nullポインターを表現ひょうげんしたい場合ばあいは、わりにオプショナルポインター使用しようします。allowzeroをつオプショナルポインターは、ポインターとおなじサイズではありません[46]

constポインター属性ぞくせいは、(ポインターではなく)ポインター参照さんしょうする変更へんこうできないことをしめします。

センチネル終端しゅうたんポインター

[編集へんしゅう]

センチネル終端しゅうたんポインター( Sentinel Terminated Pointers )。 構文こうぶん [*:x]T は、センチネルによってながさが決定けっていされるポインターを記述きじゅつします。これにより、バッファオーバーフローやオーバーリードから保護ほごされます[47]

スライス

[編集へんしゅう]

スライス( Slices )はポインターながさです。配列はいれつとスライスのちがいは、配列はいれつながさがかた一部いちぶでコンパイルにわかるのにたいして、スライスのながさは実行じっこうにわかることです。どちらも len プロパティでアクセスすることができます[48]

スライスを使つかったコードれい
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

センチネル終端しゅうたんスライス

[編集へんしゅう]

センチネル終端しゅうたんスライス( Sentinel-Terminated Slices )。 構文こうぶん[:x]Tは、実行じっこう既知きちながさをち、またながさでインデックスされた要素ようそで センチネル保証ほしょうするスライスです。このかたは、それ以前いぜんにセンチネル要素ようそがないことを保証ほしょうするものではありません。センチネル終端しゅうたんスライスはlenインデックスへのエレメントアクセスを可能かのうにします[49]

スライスを使つかったコードれい
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)とは、変数へんすう関数かんすう宣言せんげん保持ほじする名前なまえ空間くうかんとして機能きのうする構文こうぶんじょう構造こうぞうです。コンテナーは、インスタンス可能かのうかた定義ていぎでもあります。structenumunionopaque そしてZigのソースファイル自体じたいも、コンテナーのれいです。

コンテナーは、定義ていぎかこむためになみ括弧かっこ使用しようしますが、ブロックや関数かんすうとはことなります。コンテナーにはぶんふくまれていません。

[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(構造こうぞうあるいは構造こうぞうがた[50])はデータの集合しゅうごう定義ていぎするための構文こうぶんであり、フィールドにはデフォルトやメソッドをたせることができます。packed structやextern structといった特殊とくしゅ形式けいしき提供ていきょうされ、メモリレイアウトを制御せいぎょします。関数かんすうないでの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:組込くみこ関数かんすう@This]

匿名とくめいstructリテラル
[編集へんしゅう]

Zigでは、structリテラルのかた識別子しきべつしむすびついている必要ひつようはありません。結果けっか強制きょうせいされる場合ばあい、structリテラルはコピーなしで結果けっか場所ばしょ直接ちょくせつインスタンスします。 このように、識別子しきべつしむすびついていないstructリテラルを、匿名とくめいstructリテラル( Anonymous Struct Literals )あるいはタプル( Tuple )とびます[52]

Zigのenum列挙れっきょがた定義ていぎするための構文こうぶんであり、ことなるをグループします[53]順序じゅんじょ指定していせずに列挙れっきょがた宣言せんげんすることができ、任意にんい整数せいすうがた指定していしてタグがた制御せいぎょできます。 タグがた指定していしない場合ばあい自動的じどうてき整数せいすうがたてられ、0からはじまる連続れんぞくしたあたえられます。 列挙れっきょがたにはメソッドを追加ついかすることができ、switchぶん使用しようしてえることができます。 また、列挙れっきょがたリテラルを使用しようして、特定とくてい列挙れっきょ指定していすることも可能かのうです。 Zigのenumは柔軟じゅうなんせいたかく、CのABIとの互換ごかんせいたもつための機能きのう提供ていきょうされています。

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

Zigのunionは、りうる可能かのうかたのセットをフィールドのリストとして定義ていぎしますす[54]いちに1つのフィールドしかアクティブにできません。 はだかのUnionのメモリ表現ひょうげん保証ほしょうされておらず、メモリをさい解釈かいしゃくするためには@ptrCastを使用しようするか、保証ほしょうされたメモリレイアウトをつextern unionまたはpacked unionを使用しようする必要ひつようがあります。 アクティブなフィールドへのアクセスは安全あんぜんせい確認かくにんされておらず、未定義みていぎ動作どうさになります。 Union全体ぜんたいてることでのフィールドをアクティブにできます。 また、Switchぶん使用しようするためには、Unionにenumタグがたける必要ひつようがあります。 これにより、SwitchしきでUnionのペイロードを変更へんこうすることが可能かのうになります。 Unionにはenumタグがた推論すいろんさせることもでき、またstructやenumと同様どうようにメソッドをつことができます。

アクティブフィールドを参照さんしょうするとコンパイルエラーになります
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は、タグきunion( Tagged union )になり、switchしき使用しようすることができるようになります。タグきunionは、そのタグがた強制きょうせいされます[55]

タグき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ばんタグきunion
const 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 のメンバーにあたえたいときは、実体じったいしたタグをつか必要ひつようがあります。

構文こうぶんかんがえてみましょう。Cで実装じっそうするとノードの種別しゅべつ種別しゅべつごとのペイロードの共用きょうようたいになります。Zigではこれを一般いっぱんしてタグきunionひとつで実装じっそうすることが出来できます。 タグきunionを使つかうと網羅もうらせい保証ほしょうもでき、おおくの特性とくせいをメソッドとして記述きじゅつできます。たとえばキーワードや演算えんざん追加ついかした場合ばあい対応たいおうするswitchのパターンがないとエラーになりコンパイル事前じぜん変更へんこう必要ひつよう箇所かしょ確認かくにんできます。

switchしきでタグきunionの変更へんこう
[編集へんしゅう]

switchしきでタグきunionのペイロードを変更へんこうするには、変数へんすうめいまえに*をき、ポインターにします

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が定義ていぎされています。このUnionは、Int フィールドと Float フィールドをち、それぞれ整数せいすうがた浮動ふどう小数点しょうすうてんすうがた保持ほじします。また、const Self = @This()使用しようして、メソッドない共用きょうようたいかた参照さんしょうできるようにしています。

つぎに、共用きょうようたいisPositive メソッドが追加ついかされています。このメソッドは、共用きょうようたい保持ほじするせいかどうかを判定はんていします。整数せいすうがた場合ばあいは、単純たんじゅんが0よりおおきいかどうかを確認かくにんしています。浮動ふどう小数点しょうすうてんすうがた場合ばあいは、std.math.signbit使用しようして、符号ふごう判定はんていしています。符号ふごうせい場合ばあいは、せいかずであると判断はんだんします。

最後さいごに、main 関数かんすうでは、整数せいすう浮動ふどう小数点しょうすうてんすう共用きょうようたい作成さくせいし、それぞれの isPositive メソッドをして、共用きょうようたい保持ほじするせいかどうかを確認かくにんしています。結果けっか標準ひょうじゅん出力しゅつりょく出力しゅつりょくされます。

実行じっこう結果けっかでは、整数せいすうせいであり、浮動ふどう小数点しょうすうてんすうまけであるため、"i.isPositive() = true" と "f.isPositive() = false" というメッセージが表示ひょうじされます。

コードショーケース
[編集へんしゅう]
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.

Zigのopaqueは、サイズとアライメントが不明ふめい(ただしゼロではない)なあたらしいかた宣言せんげんします。structunionenum同様どうように、宣言せんげんふくめることができます。

これは、構造こうぞう詳細しょうさい公開こうかいしないCコードとやりりするさいかた安全あんぜんせいのために一般いっぱんてき使用しようされます。たとえば:

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]

ブロックの

[編集へんしゅう]

ブロックはしきです。ラベルをけると、break はブロックからかえすために使つかうことができます。

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 は、スコープをけるときに実行じっこうされるしきまたはブロックを登録とうろくします[56]複数ふくすうの 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 は、エラーが原因げんいんでスコープをけるときに実行じっこうされるしきまたはブロックを登録とうろくします[57]複数ふくすうの errdefer ぶん登録とうろくされた場合ばあい登録とうろくされたぎゃく順序じゅんじょ実行じっこうされます。 errdefer は、スコープをける原因げんいんとなったエラーコードをキャプチャーできます。

    errdefer |err| {
        std.debug.print("the error is {s}\n", .{@errorName(err)}); 
    }

キャスト

[編集へんしゅう]

Zigでは、キャスティングはひとつのかたからべつかたへの明示めいじてき変換へんかんします。この機能きのうは、ことなるかたあいだでのデータの変換へんかん操作そうさ可能かのうにし、プログラムの柔軟じゅうなんせいたかめます。Zigにはさまざまな種類しゅるいのキャストがあり、それぞれが特定とくてい目的もくてき使用しようされます[58]

以下いかでは、Zigのキャスティングについて詳細しょうさい説明せつめいします。

  1. かた強制きょうせい変換へんかん (Explicit Casts):
    • Zigでは、@bitCast@alignCast@enumFromIntなどのビルトイン関数かんすう使用しようして、明示めいじてきなキャストを実行じっこうします。
    • これらのキャストは、安全あんぜんなものとそうでないものがあります。一部いちぶのキャストはランタイムでの動作どうさ変更へんこうせず、のキャストは言語げんごレベルのアサーションを実行じっこうします。
    • たとえば、@intCast整数せいすうがたあいだでの変換へんかんおこないますが、ビットのまるめはおこないません。これにたいして、@floatCast浮動ふどう小数点しょうすうてんすうをよりちいさいサイズの浮動ふどう小数点しょうすうてんすう変換へんかんしますが、精度せいどうしなわれる可能かのうせいがあります。
  2. かた解決かいけつ (Peer Type Resolution):
    • switchifwhileforなどの文脈ぶんみゃく複数ふくすうかた存在そんざいする場合ばあい、Zigはそれらのかた解決かいけつします。これにより、すべてのかた変換へんかん可能かのうかた選択せんたくされます。
    • たとえば、switchぶんないi8i16変数へんすう追加ついかすると、その結果けっかかたi16になります。これにより、かた一貫いっかんせいち、予測よそく可能かのう動作どうさ実現じつげんされます。

これらのキャスト機能きのうは、Zigの柔軟じゅうなんせい安全あんぜんせい向上こうじょうさせ、ことなるかたあいだでのデータの変換へんかん操作そうさ容易よういにします。

  1. Type Coercion (かた変換へんかん):
    • Type Coercionは、ひとつのかた期待きたいされる場面ばめんで、ことなるかた提供ていきょうされた場合ばあい発生はっせいします。
    • test_type_coercion.zigでは、変数へんすう宣言せんげん関数かんすうかた変換へんかんしめされています。
  2. Stricter Qualification (厳格げんかく修飾しゅうしょく):
    • おなじランタイム表現ひょうげんを、修飾しゅうしょく厳密げんみつさをやすためにキャストすることができます。
  3. Integer and Float Widening (整数せいすうおよび浮動ふどう小数点しょうすうてん拡張かくちょう):
    • 整数せいすうおよび浮動ふどう小数点しょうすうてんかたが、それを表現ひょうげんできるよりおおきなかた自動的じどうてき変換へんかんされます。
  4. Float to Int (浮動ふどう小数点しょうすうてんから整数せいすうへの変換へんかん):
    • 浮動ふどう小数点しょうすうてんすう整数せいすうにキャストすることは曖昧あいまいであり、コンパイラエラーが発生はっせいします。
  5. Slices, Arrays and Pointers (スライス、配列はいれつ、ポインタの変換へんかん):
    • スライス、配列はいれつ、ポインタのあいだでの相互そうご変換へんかんしめされています。
  6. Optionals (オプショナル):
    • オプショナルがたのペイロードやnullが、オプショナルがた自体じたいにキャストされることがしめされています。
  7. Error Unions (エラーユニオン):
    • エラーコードやエラーセットがエラーユニオンがたにキャストされることがしめされています。
  8. Compile-Time Known Numbers (コンパイル既知きち数値すうち):
    • 数値すうち宛先あてさきがた表現ひょうげん可能かのうである場合ばあいにのみ、キャストがおこなわれることがしめされています。
  9. Unions and Enums (ユニオンと列挙れっきょがた):
    • タグきユニオンが列挙れっきょがたに、そして列挙れっきょがたがタグきユニオンにキャストされることがしめされています。
  10. Tuples to Arrays (タプルから配列はいれつへの変換へんかん):
    • おながたのフィールドをつタプルは、配列はいれつにキャストすることができます。
  11. Explicit Casts (明示めいじてきなキャスト):
    • @bitCast@alignCast@enumFromIntなどのビルトイン関数かんすう使用しようして明示めいじてきなキャストがおこなわれます。
  12. Peer Type Resolution (ピアがた解決かいけつ):
    • Peer Type Resolutionは、スイッチしきやifしきなどの複数ふくすうのオペランドがたあたえられた場合ばあい使用しようされます。

これらの機能きのうは、Zig言語げんごにおいてかた柔軟じゅうなん操作そうさ安全あんぜん変換へんかん可能かのうにし、コードの表現ひょうげんりょくたかめます。

かた強制きょうせい

[編集へんしゅう]

Zigにおけるかた強制きょうせい(Type Coercion)は、コンパイラがかた不一致ふいっち解消かいしょうするためにおこな自動的じどうてきかた変換へんかんのプロセスをします。Zigでは、いくつかの場面ばめんかた強制きょうせいおこなわれます。

  1. 変数へんすう代入だいにゅう: 変数へんすうがあるかた宣言せんげんされている場合ばあい、その変数へんすう代入だいにゅうされる宣言せんげんされたかたことなる場合ばあい、Zigはかた強制きょうせいおこないます。ただし、この変換へんかん安全あんぜん場合ばあいかぎります。たとえば、整数せいすうがたから浮動ふどう小数点しょうすうてんすうがたへの変換へんかん安全あんぜんですが、ぎゃく変換へんかん損失そんしつ発生はっせいする可能かのうせいがあるため、コンパイラは警告けいこくやエラーを生成せいせいすることがあります。
  2. 関数かんすう引数ひきすうかた一致いっち: 関数かんすう特定とくていかた引数ひきすう場合ばあい関数かんすうあたえられた引数ひきすうかた完全かんぜん一致いっちしない場合ばあい、Zigは適切てきせつかた変換へんかんします。これにより、かた不一致ふいっちによるエラーが回避かいひされます。
  3. 演算えんざん使用しよう: 演算えんざん使用しようするさいに、オペランドのかた一致いっちしない場合ばあい、Zigは適切てきせつかた変換へんかんして演算えんざんおこないます。たとえば、整数せいすうがた浮動ふどう小数点しょうすうてんすうがた演算えんざんおこな場合ばあい整数せいすうがた浮動ふどう小数点しょうすうてんすうがた変換へんかんされます。
  4. 明示めいじてきかた変換へんかん: プログラマが明示めいじてきかた変換へんかんしたい場合ばあい@as@intCastなどの関数かんすう使用しようして、かた変換へんかんおこなうことができます。これにより、プログラマが意図いとてきかた変換へんかんおこなうことができます。

Zigのかた強制きょうせいは、安全あんぜん変換へんかんのみをおこなうことを目指めざしており、潜在せんざいてきなデータの損失そんしつ不正確ふせいかく結果けっかふせぐために注意深ちゅういぶか設計せっけいされています。

明示めいじてきキャスト

[編集へんしゅう]

明示めいじてきキャスト(Explicit Casts)は、コンパイラにたいして特定とくていかたへの明示めいじてき変換へんかん指示しじする手段しゅだんです。これは、コンパイラによって自動的じどうてき処理しょりされるかた強制きょうせいとはことなり、プログラマが意図いとてきかた変換へんかん指定していする場合ばあい使用しようされます。Zigでは、さまざまな種類しゅるい明示めいじてきキャストが提供ていきょうされています。

以下いかは、Zigで使用しようできるおも明示めいじてきキャストのれいです:

  1. @bitCast: ビットレベルでの変換へんかんおこない、かたのビット表現ひょうげん維持いじします。このキャストは非常ひじょうていレベルであり、注意ちゅうい必要ひつようです。
  2. @alignCast: ポインタのアライメントをやします。より厳格げんかくなアライメントが必要ひつよう場合ばあい使用しようされます。
  3. @enumFromInt: 整数せいすうから列挙れっきょがた取得しゅとくします。列挙れっきょがた整数せいすう変換へんかんするぎゃく操作そうさは、自動的じどうてきかた強制きょうせいおこなわれます。
  4. @errorFromInt: 整数せいすうからエラーコードを取得しゅとくします。
  5. @errorCast: よりちいさなエラーセットに変換へんかんします。
  6. @floatCast: よりおおきな浮動ふどう小数点しょうすうてんすうがたからちいさな浮動ふどう小数点しょうすうてんすうがた変換へんかんします。
  7. @floatFromInt: 整数せいすうから浮動ふどう小数点しょうすうてん数値すうち変換へんかんします。
  8. @intCast: 整数せいすうがたあいだでの変換へんかんおこないます。
  9. @intFromBool: 真偽しんぎ整数せいすう変換へんかんします。
  10. @intFromEnum: 列挙れっきょがたのタグ整数せいすうとして取得しゅとくします。
  11. @intFromError: エラーコードを整数せいすうとして取得しゅとくします。
  12. @intFromFloat: 浮動ふどう小数点しょうすうてん数値すうち整数せいすう部分ぶぶん取得しゅとくします。
  13. @intFromPtr: ポインタのアドレスを整数せいすうとして取得しゅとくします。
  14. @ptrFromInt: 整数せいすうをポインタのアドレスに変換へんかんします。
  15. @ptrCast: ポインタがたあいだ変換へんかんおこないます。
  16. @truncate: 整数せいすうがたあいだでの変換へんかんおこない、ビットをてます。

これらの明示めいじてきなキャストは、プログラマがコンパイラにたいして特定とくていかた変換へんかん指定していする必要ひつようがある場合ばあい使用しようされます。ただし、使用しようするさいには注意ちゅうい必要ひつようであり、不適切ふてきせつなキャストがプログラムの安全あんぜんせい正確せいかくせい影響えいきょうあたえる可能かのうせいがあるため、慎重しんちょう検討けんとうする必要ひつようがあります。

ピアがた解決かいけつ

[編集へんしゅう]

Zigのピアがた解決かいけつ(Peer Type Resolution)は、複数ふくすうのオペランドのかたから、それらが共通きょうつうして可能かのうかた決定けっていするプロセスです。このプロセスは、通常つうじょう、switchやifなどの条件じょうけんしき、またはforやwhileなどのループの条件じょうけんしき使用しようされます。ピアがた解決かいけつは、これらのしき使用しようされるかた決定けっていするために、あたえられた複数ふくすうかたあいだもっと適切てきせつ共通きょうつうがたつけることを目的もくてきとしています[59]

具体ぐたいてきには、ピアがた解決かいけつ以下いか場面ばめん発生はっせいします:

  • switch しき
  • if しき
  • while しき
  • for しき
  • ブロックない複数ふくすうの break ぶん
  • 一部いちぶのバイナリ演算えんざん

ピアがた解決かいけつでは、複数ふくすうかたあいだ共通きょうつうかたつけるために、つぎのようなルールが適用てきようされます:

  • 整数せいすう場合ばあい最大さいだいのビットはば整数せいすうがた選択せんたくされます。
  • 浮動ふどう小数点しょうすうてんすう場合ばあいもっと精度せいどたか浮動ふどう小数点しょうすうてんすうがた選択せんたくされます。
  • スライスや配列はいれつなどのコンテナがた場合ばあい要素ようそかた共通きょうつう場合ばあい選択せんたくされます。
  • オプショナルがた場合ばあい共通きょうつう基底きていがた選択せんたくされます。
  • ポインタがた場合ばあい共通きょうつうのポインタがた選択せんたくされます。

これにより、Zigのコンパイラは、ことなるかた複数ふくすうのオペランドにたいしてもっと適切てきせつ共通きょうつうかた決定けっていし、かた整合せいごうせい確保かくほします。

ゼロビットがた

[編集へんしゅう]

ゼロビットがた( Zero Bit Types )、実際じっさいにはデータを保持ほじしないかたのことをします。これらのかたは、メモリない領域りょういき占有せんゆうしないため、サイズがゼロビットです[60]

Zigでは、ゼロビットがたおもかたシステムの柔軟じゅうなんせいかた安全あんぜんせいたかめるために使用しようされます。これらのかたは、実行じっこうのメモリ使用しようりょうやパフォーマンスには影響えいきょうしませんが、コードの明確めいかくさと保守ほしゅせい向上こうじょうさせるのに役立やくだちます。

アセンブリ言語げんごとの連携れんけい

[編集へんしゅう]

[TODO:所謂いわゆるインラインアセンラ]

非同期ひどうき関数かんすう

[編集へんしゅう]

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

Zigのunreachableは、制御せいぎょフローが特定とくてい位置いち到達とうたつしないことを明示めいじするために使用しようされます。具体ぐたいてきには、その部分ぶぶん到達とうたつすることがプログラムの不正ふせい動作どうさであることを表明ひょうめいします。

if (false) {
    unreachable;
}

DebugReleaseSafe モード、および zig test を使用しようする場合ばあい、unreachable は到達とうたつ不能ふのうなコードに到達とうたつしたメッセージとともに panic へのしをします[61]

ReleaseFast モードでは、オプティマイザーは到達とうたつ不能ふのうなコードはけっしてヒットしないという仮定かてい使用しようして最適さいてき実行じっこうします。しかし、ReleaseFastモードでもzigテストはunreachableをpanicへの呼出よびだしとして出力しゅつりょくします。

コンパイル

[編集へんしゅう]

unreachableのかたはnoreturnです[62]。 @TypeOf(unreachable)はコンパイルに失敗しっぱいします。 unreachableしきはコンパイルエラーになるからです。

noreturnがた

[編集へんしゅう]

noreturnは、つぎぶんかたです[63]

  • break
  • continue
  • return
  • unreachable
  • while (true) {}

ifぶしやswitchの分岐ぶんきさき( prongs )など、かた一緒いっしょ解決かいけつする場合ばあい、noreturnがたのすべてのかた互換ごかんせいがあります。

組込くみこ関数かんすう

[編集へんしゅう]

組込くみこ関数かんすう( Builtin Functions )はコンパイラによって提供ていきょうされ、接頭せっとうに @ がけられます。 パラメーターについての comptime キーワードは、そのパラメーターがコンパイル既知きちである必要ひつようがあることを意味いみします[64]

組込くみこ関数かんすう一覧いちらん
関数かんすうプロトタイプ 種別しゅべつ 説明せつめい
@addrSpaceCast(ptr: anytype) anytype
[65]
キャスト ポインターを1つのアドレス空間くうかんからべつのアドレス空間くうかん変換へんかんします。あたらしいアドレス空間くうかん結果けっかかたもとづいて推論すいろんされます。現在げんざいのターゲットとアドレス空間くうかんおうじて、このキャストは無効むこう複雑ふくざつ操作そうさ、または違法いほうである場合ばあいがあります。キャストが適法てきほうである場合ばあい結果けっかのポインターはポインターオペランドとおなじメモリー位置いちします。おなじアドレス空間くうかんあいだでのポインターのキャストはつね有効ゆうこうです。
@addWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
[66]
数値すうち演算えんざん a + b実行じっこうし、結果けっか可能かのうなオーバーフロービットをつタプルをかえします。
@alignCast(ptr: anytype) anytype
[67]
アライメント ptr*T, ?*T, または []T のいずれかです。ポインターのアライメントを変更へんこうします。使用しようするアライメントは結果けっかかたもとづいて推論すいろんされます。生成せいせいされたコードには、ポインターが約束やくそくされたようにアラインされていることを確認かくにんするためのポインターアライメント安全あんぜん検査けんさ追加ついかされます。
@alignOf(comptime T: type) comptime_int
[68]
アライメント この関数かんすうは、現在げんざいのターゲットがCのABIに適合てきごうするために、このかたがアライメントされるべきバイトすうかえします。ポインターの子供こどもかたがこのアライメントをっている場合ばあい、アライメントを省略しょうりゃくすることができます。
@as(comptime T: type, expression) T
[69]
キャスト かた強制きょうせいおこないます。このキャストは、変換へんかん曖昧あいまいでなく安全あんぜんである場合ばあい許可きょかされ、可能かのうかぎり、かたあいだ変換へんかんこのましい方法ほうほうとされています。
@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: AtomicOrder) T
[70]
不可分ふかぶん操作そうさ ポインターをアトミックにデリファレンスしてそのかえします。 T は、ポインター、bool、整数せいすう浮動ふどう小数点しょうすうてんすうあるいは enum でなければなりません。
@atomicRmw(comptime T: type, ptr: *T, comptime op: AtomicRmwOp, operand: T, comptime ordering: AtomicOrder) T
[71]
不可分ふかぶん操作そうさ メモリーをアトミックに変更へんこうしたのち以前いぜんかえします。 T は、ポインター、bool、整数せいすう浮動ふどう小数点しょうすうてんすうあるいは enum でなければなりません。
@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: AtomicOrder) void
[72]
不可分ふかぶん操作そうさ をアトミックに保存ほぞんします。 T は、ポインター、bool、整数せいすう浮動ふどう小数点しょうすうてんすうあるいは enum でなければなりません。
@bitCast(value: anytype) anytype
[73]
キャスト 1つのかたべつかた変換へんかんします。もどがた推論すいろんされた結果けっかがたです。
@bitOffsetOf(comptime T: type, comptime field_name: []const u8) comptime_int
[74]
オフセット フィールドのビットオフセットを、それをふくむ struct からの相対そうたいかえします。
@bitSizeOf(comptime T: type) comptime_int
[75]
特性とくせい かたがパックされたstruct/unionのフィールドであった場合ばあいに、Tをメモリーに格納かくのうするために必要ひつようなビットすうかえします。
@breakpoint()
[76]
デバッグ プラットフォーム固有こゆうのデバッグトラップ命令めいれい挿入そうにゅうし、デバッガがそこでブレークするようにします。
@mulAdd(comptime T: type, a: T, b: T, c: T) T
[77]
数値すうち演算えんざん せき演算えんざん(Fused multiply-add)を実行じっこうします。
@byteSwap(operand: anytype) T
[78]
キャスト オペランドのバイト順序じゅんじょ逆転ぎゃくてんさせます。
@bitReverse(integer: anytype) T
[79]
キャスト 整数せいすうのビットパターンを逆転ぎゃくてんさせます。
@offsetOf(comptime T: type, comptime field_name: []const u8) comptime_int
[80]
オフセット フィールドのバイトオフセットを、それをふくむ struct からの相対そうたいかえします。
@call(modifier: std.builtin.CallModifier, function: anytype, args: anytype) anytype
[81]
指定していされた関数かんすうします。
@cDefine(comptime name: []const u8, value) void
[82]
マクロ定義ていぎ Cマクロを定義ていぎします。
@cImport(expression) type
[83]
C インタフェース この関数かんすうは C コードを解析かいせきし、関数かんすうかた変数へんすう、および互換ごかんせいのあるマクロ定義ていぎあたらしいそら構造こうぞう体型たいけいにインポートし、そのかたかえします。expression はコンパイル解釈かいしゃくされます。この式内しきないで、@cInclude、@cDefine、@cUndef の関数かんすう動作どうさし、一時いちじバッファに追加ついかされ、それが C コードとして解析かいせきされます。通常つうじょう、アプリケーション全体ぜんたいで 1 つの @cImport のみをつべきです。これにより、コンパイラがふくすうかい clang を起動きどうするのをふせぎ、インライン関数かんすう重複じゅうふくしないようにします。複数ふくすうの @cImport しき必要ひつよう理由りゆうつぎのとおりです:
  1. シンボルの衝突しょうとつ回避かいひするため(れい:foo.h と bar.h がともに CONNECTION_COUNT を #define している場合ばあい
  2. ことなるプリプロセッサの定義ていぎで C コードを解析かいせきするため
@cInclude(comptime path: []const u8) void
[84]
C インタフェース この関数かんすうは @cImport ないでのみ発生はっせいします。これは c_import いちバッファに #include <$path>\n を追加ついかします。
@clz(operand: anytype) anytype
[85]
キャスト 整数せいすうさい上位じょういビット(ビッグエンディアンでの先頭せんとうビット)からのゼロのかずかぞえます。
@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
[86]
不可分ふかぶん操作そうさ 強力きょうりょく原子げんし比較ひかく交換こうかん操作そうさ実行じっこうし、現在げんざい指定していされた期待きたいでない場合ばあいはnullをかえします。
@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
[87]
不可分ふかぶん操作そうさ よわ原子げんし比較ひかく交換こうかん操作そうさ実行じっこうし、現在げんざい指定していされた期待きたいでない場合ばあいはnullをかえします。
@compileError(comptime msg: []const u8) noreturn
[88]
コンパイルエラー 意味いみ解析かいせきされると、メッセージmsgをつコンパイルエラーが発生はっせいします。
@compileLog(args: ...) void
[89]
コンパイルログ 引数ひきすうをコンパイル出力しゅつりょくします。
@constCast(value: anytype) DestType
[90]
キャスト ポインターからconst修飾しゅうしょく削除さくじょします。
@ctz(operand: anytype) anytype
[91]
キャスト 整数せいすう最下位さいかいビット(ビッグエンディアンでの末尾まつびビット)からのゼロのかずかぞえます。
@cUndef(comptime name: []const u8) void
[92]
マクロ定義ていぎ解除かいじょ Cマクロを無効むこうにします。
@cVaArg(operand: *std.builtin.VaList, comptime T: type) T
[93]
可変かへん引数ひきすう Cマクロva_argを実装じっそうします。
@cVaCopy(src: *std.builtin.VaList) std.builtin.VaList
[94]
可変かへん引数ひきすう Cマクロva_copyを実装じっそうします。
@cVaEnd(src: *std.builtin.VaList) void
[95]
可変かへん引数ひきすう Cマクロva_endを実装じっそうします。
@cVaStart() std.builtin.VaList
[96]
可変かへん引数ひきすう Cマクロva_startを実装じっそうします。
@divExact(numerator: T, denominator: T) T
[97]
数値すうち演算えんざん 完全かんぜん除算じょざん。デノミネーター != 0 であり、@divTrunc(numerator, denominator) * denominator == numerator を保証ほしょうする必要ひつようがあります。
@divFloor(numerator: T, denominator: T) T
[98]
数値すうち演算えんざん ゆかきの除算じょざんまけ無限むげんだいかってまるめます。符号ふごうなし整数せいすう場合ばあい分子ぶんし / 分母ぶんぼおなじです。
@divTrunc(numerator: T, denominator: T) T
[99]
数値すうち演算えんざん 除算じょざん。ゼロにかってまるめます。符号ふごうなし整数せいすう場合ばあい分子ぶんし / 分母ぶんぼおなじです。
@embedFile(comptime path: []const u8) *const [N:0]u8
[100]
ファイル操作そうさ ファイルの内容ないようつ、ヌル終端しゅうたん固定こていサイズ配列はいれつへのコンパイル定数ていすうポインターをかえします。
@enumFromInt(integer: anytype) anytype
[101]
キャスト 整数せいすう列挙れっきょ変換へんかんします。
@errorFromInt(value: std.meta.Int(.unsigned, @bitSizeOf(anyerror))) anyerror
[102]
キャスト エラーの整数せいすう表現ひょうげんからグローバルエラーセットがた変換へんかんします。
@errorName(err: anyerror) [:0]const u8
[103]
文字もじれつ操作そうさ エラーの文字もじれつ表現ひょうげんかえします。
@errorReturnTrace() ?*builtin.StackTrace
[104]
エラー処理しょり エラーのがえ追跡ついせきされている場合ばあい、スタックトレースオブジェクトをかえします。それ以外いがい場合ばあいはnullをかえします。
@errorCast(value: anytype) anytype
[105]
キャスト エラーセットまたはエラーユニオンのべつのエラーセットに変換へんかんします。
@export(declaration, comptime options: std.builtin.ExportOptions) void
[106]
シンボル出力しゅつりょく 出力しゅつりょくオブジェクトファイルにシンボルを作成さくせいします。
@extern(T: type, comptime options: std.builtin.ExternOptions) T
[107]
外部がいぶシンボル参照さんしょう 出力しゅつりょくオブジェクトファイルに外部がいぶシンボルへの参照さんしょう作成さくせいします。
@fence(order: AtomicOrder) void
[108]
同期どうき操作そうさ 発生はっせい前後ぜんこうのエッジを導入どうにゅうするために使用しようされます。
@field(lhs: anytype, comptime field_name: []const u8) (field)
[109]
フィールドアクセス コンパイル文字もじれつでフィールドアクセスを実行じっこうします。
@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: *T) *ParentType
[110]
ポインター操作そうさ フィールドへのポインターから、構造こうぞうたいのベースポインターをかえします。
@floatCast(value: anytype) anytype
[111]
キャスト 1つの浮動ふどう小数点しょうすうてんすうがたからべつ浮動ふどう小数点しょうすうてんすうがた変換へんかんします。
@floatFromInt(int: anytype) anytype
[112]
キャスト 整数せいすうもっとちか浮動ふどう小数点しょうすうてん数表現すうひょうげん変換へんかんします。
@frameAddress() usize
[113]
デバッグ 現在げんざいのスタックフレームのベースポインターをかえします。
@hasDecl(comptime Container: type, comptime name: []const u8) bool
[114]
メタ情報じょうほう コンテナに指定していされた名前なまえ宣言せんげんがあるかどうかをかえします。
@hasField(comptime Container: type, comptime name: []const u8) bool
[115]
メタ情報じょうほう 構造こうぞうたい共用きょうようたい、または列挙れっきょがた指定していされたフィールドめい存在そんざいするかどうかをかえします。
@import(comptime path: []const u8) type
[116]XXX
ビルド管理かんり path に対応たいおうする Zig ファイルをつけ、まだ追加ついかされていない場合ばあいにビルドに追加ついかします。
@inComptime() bool
[117]
メタ情報じょうほう このビルトインがコンパイル実行じっこうされたかどうかをかえします。
@intCast(int: anytype) anytype
[118]
キャスト 数値すうちおな数値すうちとしてのみ変換へんかんします。
@intFromBool(value: bool) u1
[119]
キャスト true を @as(u1, 1)、false を @as(u1, 0) に変換へんかんします。
@intFromEnum(enum_or_tagged_union: anytype) anytype
[120]
キャスト 列挙れっきょ整数せいすうのタグがた変換へんかんします。
@intFromError(err: anytype) std.meta.Int(.unsigned, @bitSizeOf(anyerror))
[121]
キャスト エラーをエラーの整数せいすう表現ひょうげん変換へんかんします。
@intFromFloat(float: anytype) anytype
[122]
キャスト 浮動ふどう小数点しょうすうてんすう整数せいすう部分ぶぶん変換へんかんします。
@intFromPtr(value: anytype) usize
[123]
キャスト ポインターを usize に変換へんかんします。
@max(a: T, b: T) T
[124]
数値すうち演算えんざん a と b の最大さいだいかえします。
@memcpy(noalias dest, noalias source) void
[125]
メモリ操作そうさ メモリの領域りょういきべつ領域りょういきにコピーします。
@memset(dest, elem) void
[126]
メモリ操作そうさ メモリ領域りょういきぜん要素ようそを elem に設定せっていします。
@min(a: T, b: T) T
[127]
数値すうち演算えんざん a と b の最小さいしょうかえします。
@wasmMemorySize(index: u32) u32
[128]
メモリ操作そうさ 指定していされたインデックスの Wasm メモリのサイズを Wasm ページ単位たんいかえします。
@wasmMemoryGrow(index: u32, delta: u32) i32
[129]
メモリ操作そうさ 指定していされたインデックスの Wasm メモリのサイズをやします。
@mod(numerator: T, denominator: T) T
[130]
数値すうち演算えんざん 剰余じょうよかえします。
@mulWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
[131]
数値すうち演算えんざん オーバーフロー可能かのうざんおこない、結果けっか可能かのうなオーバーフローのビットをかえします。
@panic(message: []const u8) noreturn
[132]
デバッグ パニックハンドラー関数かんすうします。
@popCount(operand: anytype) anytype
[133]
ビット操作そうさ 整数せいすうなかでセットされたビットのかずかぞえます。
@prefetch(ptr: anytype, comptime options: PrefetchOptions) void
[134]
メモリアクセス プリフェッチ命令めいれい発行はっこうします。
@ptrCast(value: anytype) anytype
[135]
キャスト 1つのかたのポインターをべつかたのポインターに変換へんかんします。
@ptrFromInt(address: usize) anytype
[136]
キャスト 整数せいすうをポインターに変換へんかんします。
@rem(numerator: T, denominator: T) T
[137]
数値すうち演算えんざん 剰余じょうよ計算けいさんおこないます。符号ふごうなし整数せいすう場合ばあい、これは numerator % denominator とおなじです。がわは denominator > 0 を保証ほしょうする必要ひつようがあります。それ以外いがい場合ばあい、ランタイムセーフティチェックが有効ゆうこうになっている場合ばあい操作そうさ除算じょざんゼロによる剰余じょうよ発生はっせいします。
@returnAddress() usize
[138]
デバッグ この関数かんすうは、現在げんざい関数かんすうがリターンしたときに実行じっこうされるつぎのマシンコード命令めいれいのアドレスをかえします。
@select(comptime T: type, pred: @Vector(len, bool), a: @Vector(len, T), b: @Vector(len, T)) @Vector(len, T)
[139]
条件じょうけん選択せんたく pred にもとづいて a または b から要素ようそ選択せんたくします。pred[i] が true の場合ばあい結果けっか対応たいおうする要素ようそは a[i] になり、そうでなければ b[i] になります。
@setAlignStack(comptime alignment: u29) void
[140]
スタックアライメント 関数かんすうのスタックアライメントがすくなくとも指定していされたバイトすうになるように保証ほしょうします。
@setCold(comptime is_cold: bool) void
[141]
最適さいてき 現在げんざい関数かんすうが(またはされない)まれにされることを最適さいてきプログラムにつたえます。この関数かんすう関数かんすうスコープないでのみ有効ゆうこうです。
@setEvalBranchQuota(comptime new_quota: u32) void
[142]
コンパイルコード コンパイルのコード実行じっこう使用しようするバックワードブランチの最大さいだいすうやします。あたらしいクォータがデフォルトのクォータ(1000)よりちいさい場合ばあいや、以前いぜん明示めいじてき設定せっていされたクォータよりもちいさい場合ばあいは、無視むしされます。
@setFloatMode(comptime mode: FloatMode) void
[143]
浮動ふどう小数点しょうすうてんモード 浮動ふどう小数点しょうすうてん演算えんざん定義ていぎ方法ほうほうかんする現在げんざいのスコープのルールを変更へんこうします。
@setRuntimeSafety(comptime safety_on: bool) void
[144]
ランタイムセーフティ 関数かんすうしをふくむスコープでランタイムセーフティチェックが有効ゆうこうかどうかを設定せっていします。
@shlExact(value: T, shift_amt: Log2T) T
[145]
数値すうち演算えんざん ひだりシフト演算えんざん(<<)を実行じっこうします。符号ふごうなし整数せいすう場合ばあい、シフトアウトされる任意にんいの1ビットがあると結果けっか未定義みていぎです。符号ふごう整数せいすう場合ばあい結果けっかは、結果けっか符号ふごうビットと一致いっちしないビットがシフトアウトされた場合ばあいには未定義みていぎです。
@shlWithOverflow(a: anytype, shift_amt: Log2T) struct { @TypeOf(a), u1 }
[146]
数値すうち演算えんざん a << b を実行じっこうし、結果けっかとオーバーフロービットの可能かのうせいがあるタプルをかえします。
@shrExact(value: T, shift_amt: Log2T) T
[147]
数値すうち演算えんざん みぎシフト演算えんざん(>>)を実行じっこうします。がわは、シフトが任意にんいの1ビットをアウトさせないことを保証ほしょうします。
@shuffle(comptime E: type, a: @Vector(a_len, E), b: @Vector(b_len, E), comptime mask: @Vector(mask_len, i32)) @Vector(mask_len, E)
[148]
ベクトル操作そうさ マスクにもとづいて a と b から要素ようそ選択せんたくしてあたらしいベクトルを構築こうちくします。
@sizeOf(comptime T: type) comptime_int
[149]
メモリ操作そうさ メモリないで T を格納かくのうするために必要ひつようなバイトすうかえします。
@splat(scalar: anytype) anytype
[150]
ベクトル操作そうさ かく要素ようそがスカラーであるベクトルを生成せいせいします。
@reduce(comptime op: std.builtin.ReduceOp, value: anytype) E
[151]
ベクトル操作そうさ 指定していされた演算えんざん op を使用しようして、要素ようそ水平すいへいリダクションを実行じっこうしてスカラーかたE)に変換へんかんします。
@src() std.builtin.SourceLocation
[152]
デバッグ 関数かんすう名前なまえとソースコードない場所ばしょあらわす SourceLocation 構造こうぞうたいかえします。
@sqrt(value: anytype) @TypeOf(value)
[153]
数値すうち演算えんざん 浮動ふどう小数点しょうすうてんすう平方根へいほうこん計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@sin(value: anytype) @TypeOf(value)
[154]
数値すうち演算えんざん ほうでの正弦せいげんさんかく関数かんすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@cos(value: anytype) @TypeOf(value)
[155]
数値すうち演算えんざん ほうでの余弦よげんさんかく関数かんすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@tan(value: anytype) @TypeOf(value)
[156]
数値すうち演算えんざん ほうでの正接せいせつさんかく関数かんすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@exp(value: anytype) @TypeOf(value)
[157]
数値すうち演算えんざん 自然しぜん対数たいすうそこeの指数しすう関数かんすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@exp2(value: anytype) @TypeOf(value)
[158]
数値すうち演算えんざん そこが2の指数しすう関数かんすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@log(value: anytype) @TypeOf(value)
[159]
数値すうち演算えんざん 浮動ふどう小数点しょうすうてんすう自然しぜん対数たいすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@log2(value: anytype) @TypeOf(value)
[160]
数値すうち演算えんざん そこが2の対数たいすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@log10(value: anytype) @TypeOf(value)
[161]
数値すうち演算えんざん そこが10の対数たいすう計算けいさんします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@abs(value: anytype) anytype
[162]
数値すうち演算えんざん 整数せいすうまたは浮動ふどう小数点しょうすうてんすう絶対ぜったいかえします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@floor(value: anytype) @TypeOf(value)
[163]
数値すうち演算えんざん 指定していされた浮動ふどう小数点しょうすうてんすうよりおおきくない最大さいだい整数せいすうかえします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@ceil(value: anytype) @TypeOf(value)
[164]
数値すうち演算えんざん 指定していされた浮動ふどう小数点しょうすうてんすうよりちいさくない最小さいしょう整数せいすうかえします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@trunc(value: anytype) @TypeOf(value)
[165]
数値すうち演算えんざん 指定していされた浮動ふどう小数点しょうすうてんすう整数せいすうまるめ、ゼロにかってまるめます。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@round(value: anytype) @TypeOf(value)
[166]
数値すうち演算えんざん 指定していされた浮動ふどう小数点しょうすうてんすう整数せいすうまるめ、ゼロからとおざけます。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@subWithOverflow(a: anytype, b: anytype) struct { @TypeOf(a, b), u1 }
[167]
数値すうち演算えんざん a - b を実行じっこうし、結果けっか可能かのうなオーバーフロービットをつタプルをかえします。
@tagName(value: anytype) [:0]const u8
[168]
データ変換へんかん 列挙れっきょまたはユニオンを、名前なまえあらわ文字もじれつリテラルに変換へんかんします。
@This() type
[169]
メタプログラミング 現在げんざい関数かんすうしの内部ないぶもっとふか構造こうぞうたい列挙れっきょたい、または共用きょうようたいかえします。
@trap() noreturn
[170]
デバッグ プログラムを異常いじょう終了しゅうりょうさせるためにプラットフォーム固有こゆうのトラップ/ジャム命令めいれい挿入そうにゅうします。
@truncate(integer: anytype) anytype
[171]
数値すうち演算えんざん 整数せいすうがたからビットをて、よりちいさいまたはおなじサイズの整数せいすうがた生成せいせいします。利用りよう可能かのう場合ばあい専用せんようのハードウェア命令めいれい使用しようします。
@Type(comptime info: std.builtin.Type) type
[172]
メタプログラミング かた情報じょうほうかたさい構成こうせいします。
@typeInfo(comptime T: type) std.builtin.Type
[173]
メタプログラミング かたのリフレクション情報じょうほう提供ていきょうします。
@typeName(T: type) *const [N:0]u8
[174]
メタプログラミング かた文字もじれつ表現ひょうげんかえします。
@TypeOf(...) type
[175]
メタプログラミング しきかたかえします。
@unionInit(comptime Union: type, comptime active_field_name: []const u8, init_expr) Union
[176]
メタプログラミング ユニオンの初期しょき構文こうぶんおなじですが、フィールドめい識別子しきべつしトークンではなく、コンパイル既知きちです。
@Vector(len: comptime_int, Element: type) type
[177]
データ構造こうぞう ベクトルを作成さくせいします。
@volatileCast(value: anytype) DestType
[178]
データ変換へんかん ポインタからvolatile修飾しゅうしょく削除さくじょします。
@workGroupId(comptime dimension: u32) u32
[179]
プラットフォーム依存いぞん 指定していされた次元じげんのカレントカーネルないのワークグループのインデックスをかえします。
@workGroupSize(comptime dimension: u32) u32
[180]
プラットフォーム依存いぞん 指定していされた次元じげんのワークグループがつワークアイテムのかずかえします。
@workItemId(comptime dimension: u32) u32
[181]
プラットフォーム依存いぞん 指定していされた次元じげんのワークグループないのワークアイテムのインデックスをかえします。この関数かんすうは0(ふくむ)から @workGroupSize(dimension)排他はいたてき)までのかえします。

ビルドモード

[編集へんしゅう]

Zigのビルドモードは、プログラムをビルドするさい特定とくてい設定せっていとオプションのわせを指定していするものです。Zigにはつぎの4つのビルドモードがあります。

  1. Debug(デフォルト)
  2. ReleaseFast
  3. ReleaseSafe
  4. ReleaseSmall

それぞれのビルドモードは、ことなる目的もくてき対応たいおうしています。たとえば、デバッグちゅうには実行じっこうのパフォーマンスよりもデバッグしやすさや安全あんぜんせい重要じゅうようですが、リリースには実行じっこうのパフォーマンスやバイナリサイズの最適さいてき優先ゆうせんされることがあります。

ビルドモードを指定していするには、zig build-exeコマンドを使用しようします。たとえば、ReleaseFastモードでプログラムをビルドするには、つぎのようにします。

$ zig build-exe example.zig -O ReleaseFast

かくビルドモードの特性とくせい以下いかとおりです。

  1. Debug:
    • 実行じっこうのパフォーマンスはおそ
    • 安全あんぜんせいチェックが有効ゆうこう
    • ビルド速度そくどはや
    • バイナリサイズがおおきい
  2. ReleaseFast:
    • 実行じっこうのパフォーマンスがはや
    • 安全あんぜんせいチェックが無効むこう
    • ビルド速度そくどおそ
    • バイナリサイズがおおきい
    • 再現さいげん可能かのうなビルド
  3. ReleaseSafe:
    • 実行じっこうのパフォーマンスはちゅう程度ていど
    • 安全あんぜんせいチェックが有効ゆうこう
    • ビルド速度そくどおそ
    • バイナリサイズがおおきい
    • 再現さいげん可能かのうなビルド
  4. ReleaseSmall:
    • 実行じっこうのパフォーマンスはちゅう程度ていど
    • 安全あんぜんせいチェックが無効むこう
    • ビルド速度そくどおそ
    • バイナリサイズがちいさい
    • 再現さいげん可能かのうなビルド

これらのビルドモードは、プロジェクトのニーズや要件ようけんおうじて適切てきせつなものを選択せんたくすることが重要じゅうようです。

メモリー管理かんり

[編集へんしゅう]

Zigのメモリー管理かんり基礎きそについて、重要じゅうようなポイントを要約ようやくします。

  1. メモリー管理かんり責任せきにん: Zig言語げんごでは、プログラマーがメモリー管理かんりおこないます。つまり、Zigにはランタイムがないため、リアルタイムソフトウェア、オペレーティングシステムカーネル、みデバイス、てい遅延ちえんサーバーなど、さまざまな環境かんきょうでZigコードがシームレスに動作どうさします。
  2. アロケーターの使用しよう: Zigでは、C言語げんごとはことなり、デフォルトのアロケーターは提供ていきょうされません。わりに、アロケートが必要ひつよう関数かんすうは Allocator パラメーターをれます。データ構造こうぞう同様どうように、初期しょき関数かんすうで Allocator パラメーターをれます。
  3. アロケーターの選択せんたく: どのアロケーターを使用しようするかは、さまざまな要因よういん依存いぞんします。ライブラリを作成さくせいする場合ばあいやlibcをリンクする場合ばあい、またはメモリー要件ようけん既知きち場合ばあいなど、適切てきせつなアロケーターを選択せんたくするためのフローチャートが提供ていきょうされています。
  4. メモリーの場所ばしょ: Zigでは、ことなる種類しゅるいのデータがことなる場所ばしょ配置はいちされます。たとえば、関数かんすうないのvar宣言せんげんはその関数かんすうのスタックフレームに配置はいちされます。一方いっぽう、グローバルレベルや構造こうぞう体内たいないのvar宣言せんげんはグローバルデータセクションに配置はいちされます。
  5. アロケーターの実装じっそう: Zigプログラマーは、Allocator インターフェースをたすことで独自どくじのアロケーターを実装じっそうできます。これには、allocFnとresizeFnを提供ていきょうする必要ひつようがあります。
  6. 再帰さいき: 再帰さいきは、Zigでモデリングソフトウェアにおいて基本きほんてきなツールですが、制限せいげんのメモリー問題もんだいがあります。現在げんざい再帰さいき通常つうじょうどおり動作どうさしますが、将来しょうらいのZigバージョンではスタックオーバーフローからの保護ほご提供ていきょうされる予定よていです。
  7. ライフタイムと所有しょゆうけん: ポインターがすメモリが使用しようできなくなった場合ばあいに、そのポインターにアクセスしないようにすることが、Zigプログラマーの責任せきにんです。所有しょゆうけんとライフタイムのセマンティクスは、ポインターの所有しょゆうしゃとメモリーの使用しよう可能かのう期間きかん明確めいかくにするために、関数かんすうやデータ構造こうぞうのAPIドキュメントで説明せつめいされるべきです。

Zigは、メモリー管理かんりかんする柔軟じゅうなんせい制御せいぎょ提供ていきょうし、プログラマーがそのアプリケーションのニーズにわせて最適さいてきなアロケーターを選択せんたくできるようにしています。

メモリー管理かんり責任せきにんとアロケーター

[編集へんしゅう]

Zig言語げんごはプログラマーにわってメモリー管理かんりおこなうことはありません。このため、Zigにはランタイムがなく、Zigのコードはリアルタイム・ソフトウェア、OSカーネル、組込くみこ機器ききてい遅延ちえんサーバーなど、おおくの環境かんきょうでシームレスに動作どうさします。その結果けっか、Zigのプログラマーはつねつぎのようないにこたえられなければなりません[182]

バイトはどこにあるのか?

Zigと同様どうよう、C言語げんごもメモリー管理かんり手動しゅどうおこなっています。しかし、Zigとはことなり、C言語げんごにはmalloc、realloc、freeというデフォルトのアロケーターがあります。libc とリンクするとき、Zig はこのアロケーターを std.heap.c_allocator で公開こうかいします。しかし、慣習かんしゅうとして、Zig にはデフォルトのアロケーターはありません。わりに、割当わりあてが必要ひつよう関数かんすうは Allocator パラメーターをります。同様どうように、std.ArrayList のようなデータ構造こうぞうもその初期しょき関数かんすうで Allocator パラメーターをけます。

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"

上記じょうきれいでは、スタックじょうの 100 バイトのメモリーが FixedBufferAllocator の初期しょき使つかわれ、それが関数かんすうわたされます。

利便りべんせいのために std.testing.allocator でグローバルな FixedBufferAllocator が用意よういされており、基本きほんてきなリーク検出けんしゅつおこなうことができます。

Zig には std.heap.GeneralPurposeAllocator でインポート可能かのう汎用はんようアロケーターがあります。しかし、やはり、アロケーターのえらかた指針ししんしたがうことが推奨すいしょうされます。

アロケーターのえらかた

[編集へんしゅう]

どのアロケーターを使つかうかは、様々さまざま要因よういんによってまります。以下いかは、判断はんだんのためのフローチャートです[183]

  1. ライブラリーをつくっているのですか?この場合ばあい、パラメーターとしてアロケーターをり、ライブラリーのユーザーにどのアロケーターを使つかうかをゆだねるのがベストです。
  2. libc をリンクしていますか? この場合ばあいすくなくともメインのアロケーターは std.heap.c_allocator がただしい選択せんたくだとおもわれます。
  3. 必要ひつようなバイトすう最大さいだいは、コンパイルかっているかず制限せいげんされていますか? この場合ばあい、スレッドセーフが必要ひつようかどうかによって std.heap.FixedBufferAllocator か std.heap.ThreadSafeFixedBufferAllocator を使つかってください。
  4. あなたのプログラムはコマンドラインアプリケーションで、基本きほんてき循環じゅんかんパターンをたずに最初さいしょから最後さいごまで実行じっこうされ(ビデオゲームのメインループやウェブサーバーのリクエストハンドラーなど)、最後さいごにすべてをいち解放かいほうすることに意味いみがあるようなものでしょうか?このような場合ばあい、このパターンにしたがうことをおすすめします。
    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()をすと、すべてがいち解放かいほうされます。
  5. ビデオゲームのメインループやウェブサーバのリクエストハンドラのような周期しゅうきてきなパターンの一部いちぶでしょうか?たとえば、ビデオゲームのフレームが完全かんぜんにレンダリングされたのちや、ウェブサーバーのリクエストが処理しょりされたのちなど、サイクルのわりにすべてのアロケーションをいち解放かいほうできる場合ばあい、std.heap.ArenaAllocator は素晴すばらしい候補こうほとなります。まえ箇条書かじょうがきでしめしたように、これによってアリーナ全体ぜんたいいち解放かいほうすることができます。また、メモリの上限じょうげん設定せっていできる場合ばあいは、std.heap.FixedBufferAllocator を使用しようすると、さらに最適さいてきできることに注意ちゅういしましょう。
  6. テストをいていて、error.OutOfMemoryがただしく処理しょりされることを確認かくにんしたいですか?この場合ばあいは std.testing.FailingAllocator を使つかってください。
  7. テストをいていますか?この場合ばあいは std.testing.allocator を使つかってください。
  8. 最後さいごに、上記じょうきのどれにもてはまらない場合ばあいは、汎用はんようのアロケーターが必要ひつようです。Zigの汎用はんようアロケーターは、設定せっていオプションのcomptime構造こうぞうたいり、かたかえ関数かんすうとして提供ていきょうされている。一般いっぱんてきには、メイン関数かんすうに std.heap.GeneralPurposeAllocator をひとつセットアップし、アプリケーションの様々さまざま部分ぶぶんにそのアロケーターやサブアロケーターをわたしていくことになる。
  9. アロケーターの実装じっそう検討けんとうすることもできます。

バイトはどこにあるのか?

[編集へんしゅう]

Where are the bytes?

”foo” のような文字もじれつリテラルは、グローバル定数ていすうデータセクション( global constant data section )にあります。このため、文字もじれつリテラルをミュータブルスライスにわたすとエラーになります[184]

文字もじれつリテラルと同様どうように、コンパイルかっているconst宣言せんげんは、グローバル定数ていすうデータセクションに格納かくのうされます。また、コンパイル変数へんすうもグローバル定数ていすうデータセクションに格納かくのうされます。

関数かんすうないのvar宣言せんげんは、その関数かんすうのスタックフレームに格納かくのうされます。関数かんすうからもどると、関数かんすうのスタックフレームにある変数へんすうへのポインターは無効むこう参照さんしょうとなり、その参照さんしょう解除かいじょ確認かくにん定義ていぎ動作どうさとなります。

アロケーターの実装じっそう

[編集へんしゅう]

Zig プログラマーは Allocator インターフェースをたすことで、自分じぶん自身じしんのアロケーターを実装じっそうすることができます。そのためには、std/mem.zig のドキュメントコメントをよくんで、allocFn と resizeFn を指定していする必要ひつようがあります[185]

インスピレーションをるために、おおくのアロケータのれいることができます。std/heap.zig と std.heap.GeneralPurposeAllocator をてください。

ヒープアロケーションの失敗しっぱい

[編集へんしゅう]

おおくのプログラミング言語げんごでは、ヒープてに失敗しっぱいした場合ばあい無条件むじょうけんにクラッシュすることで対処たいしょすることにしています[186]

Zig のプログラマは、慣習かんしゅうとして、これが満足まんぞくのいく解決かいけつさくであるとはかんがえていません。

そのわり、error.OutOfMemoryはヒープての失敗しっぱいあらわし、Zigライブラリーはヒープての失敗しっぱい処理しょり正常せいじょう完了かんりょうしなかったときはいつでもこのエラーコードをかえします。

Linuxなどの一部いちぶのOSでは、デフォルトでメモリーのオーバーコミットが有効ゆうこうになっているため、ヒープての失敗しっぱい処理しょりすることは無意味むいみであると主張しゅちょうするひともいます。この理由りゆうにはおおくの問題もんだいがあります。

  1. オーバーコミット機能きのうつのは一部いちぶのOSだけである。
    • Linuxはデフォルトで有効ゆうこうになっていますが、設定せってい可能かのうです。
    • Windowsはオーバーコミットしません。
    • 組込くみこみシステムはオーバーコミットしません。
    • ホビーようOSはオーバーコミットがあってもなくてもよい。
  2. リアルタイムシステムでは、オーバーコミットがないだけでなく、通常つうじょう、アプリケーションごとの最大さいだいメモリ容量ようりょうがあらかじめめられています。
  3. ライブラリーを作成さくせいする場合ばあい、コードのさい利用りようおも目的もくてきの1つです。ライブラリーの作成さくせいにおいて、コードのさい利用りよう重要じゅうよう目的もくてきひとつである。
  4. オーバーコミットが有効ゆうこうであることに依存いぞんしているソフトウェアもありますが、その存在そんざいかぞれないほどのユーザー体験たいけん破壊はかい原因げんいんとなっています。オーバーコミットを有効ゆうこうにしたシステム、たとえばLinuxのデフォルト設定せっていでは、メモリーが枯渇こかつしそうになると、システムがロックして使つかえなくなる。このとき、OOM Killer はヒューリスティックにもとづき kill するアプリケーションを選択せんたくします。この非決定ひけっていてき判断はんだんにより、重要じゅうようなプロセスが強制きょうせい終了しゅうりょうされることがおおく、システムを正常せいじょうもどすことができないことがよくあります。

再帰さいき

[編集へんしゅう]

再帰さいき( Recursion )はソフトウェアをモデリングするさい基本きほんてきなツールである。しかし、再帰さいきにはしばしば見落みおとされがちな問題もんだいがあります[187]

再帰さいきはZigで活発かっぱつ実験じっけんされている分野ぶんやであり、ここにある文書ぶんしょ最終さいしゅうてきなものではありません。0.3.0のリリースノートで、再帰さいき状況じょうきょう要約ようやくしてむことができます。

簡単かんたんにまとめると、現在げんざいのところ再帰さいき期待きたいどおりに動作どうさしています。Zigのコードはまだスタックオーバーフローから保護ほごされていませんが、Zigの将来しょうらいのバージョンでは、Zigのコードからのある程度ていど協力きょうりょく必要ひつようですが、そのような保護ほご提供ていきょうすることが予定よていされています。

ライフタイムとオーナーシップ

[編集へんしゅう]

ポインターをしているメモリーが利用りようできなくなったときに、ポインターにアクセスしないようにするのは、Zigのプログラマーの責任せきにんです。スライスは、のメモリーを参照さんしょうするというてんで、ポインターの一種いっしゅであることに注意ちゅういしてください [188]

バグをふせぐために、ポインターをあつかうときにしたがうと便利べんり規則きそくがあります。一般いっぱんに、関数かんすうがポインターをかえ場合ばあい、その関数かんすうのドキュメントでは、だれがそのポインターを「所有しょゆう」しているかを説明せつめいする必要ひつようがあります。この概念がいねんは、プログラマーがポインターを解放かいほうすることが適切てきせつである場合ばあい、そのタイミングを判断はんだんするのに役立やくだちます。

たとえば、関数かんすうのドキュメントに「かえされたメモリーはもと所有しょゆうする」とかれていた場合ばあい、その関数かんすうすコードは、いつそのメモリーを解放かいほうするかという計画けいかくっていなければなりません。このような場合ばあい、おそらく関数かんすうは Allocator パラメーターをります。

ときには、ポインターの寿命じゅみょうはもっと複雑ふくざつ場合ばあいがあります。たとえば、std.ArrayList(T).items スライスは、あたらしい要素ようそ追加ついかするなどしてリストのサイズがつぎ変更へんこうされるまで有効ゆうこうです。

関数かんすうやデータ構造こうぞうのAPIドキュメントでは、ポインターの所有しょゆうけん有効ゆうこう期限きげんについて細心さいしん注意ちゅういはらって説明せつめいする必要ひつようがあります。所有しょゆうけんとは、ポインターが参照さんしょうするメモリーを解放かいほうする責任せきにんだれにあるかということであり、寿命じゅみょうとは、メモリーがアクセス不能ふのうになる時点じてん未定義みていぎ動作どうさ発生はっせいしないように)をめることです。

C言語げんごとの相互そうご運用うんよう

[編集へんしゅう]

ZigはC言語げんごから独立どくりつしており、おおくの言語げんごとはことなりlibcに依存いぞんしませんが、Zigは既存きそんのC言語げんごかれたとの相互そうご作用さよう重要じゅうようせいみとめています[189]

ZigはC言語げんごとの相互そうご運用うんよう容易よういにするために、いくつかの方法ほうほう用意よういしています。

C言語げんごがたプリミティブ

[編集へんしゅう]

以下いかしめすZigのかたはC言語げんごとのABI互換ごかんせい保証ほしょうされており、かた同様どうよう使用しようすることができます[190]

C の void がた相互そうご運用うんようする場合ばあいanyopaque使用しようします。

C言語げんごヘッダーからのインポート

[編集へんしゅう]

組込くみこ関数かんすう @cImport使用しようすると、.h ファイルからシンボルを直接ちょくせつインポートすることができます[191]。 @cImport 関数かんすうは、パラメーターとしてしき受取うけとります。このしきはコンパイル評価ひょうかされ、プリプロセッサー指令しれい制御せいぎょ複数ふくすうの.hファイルをインクルードするために使用しようされます。

C言語げんご翻訳ほんやく CLI

[編集へんしゅう]
コマンドライン・フラグ
[編集へんしゅう]
target と -cflags の使用しよう
[編集へんしゅう]
cImportとtranslate-cの比較ひかく
[編集へんしゅう]

C言語げんご翻訳ほんやくキャッシュ

[編集へんしゅう]

翻訳ほんやく失敗しっぱい

[編集へんしゅう]

C言語げんごマクロ

[編集へんしゅう]

C言語げんごポインター

[編集へんしゅう]

C言語げんごライブラリーのエクスポート

[編集へんしゅう]

オブジェクトファイルの混在こんざい

[編集へんしゅう]

キーワード一覧いちらん

[編集へんしゅう]
align
ポインターのアライメントを指定していするために使用しようします。
また、変数へんすう関数かんすう宣言せんげんのち使用しようして、その変数へんすう関数かんすうへのポインターのアライメントを指定していすることができます。
allowzero
ポインターの属性ぞくせい allowzero は、ポインターのアドレスが 0 であることを許可きょかします。
and
論理ろんりせき
anyframe
関数かんすうフレームへのポインター保持ほじする変数へんすうかたとして使用しようすることができます。
anytype
関数かんすうのパラメーターは、かたわりにanytypeで宣言せんげんすることができます。かた関数かんすう呼出よびだされた場所ばしょ推測すいそくされます
asm
インラインアセンブリしき開始かいしします。これにより、コンパイル生成せいせいされる機械きかい直接ちょくせつ制御せいぎょすることが