zigが扱う型を把握した上で宣言したい

zig

環境

当方の環境は以下である。
zig version: 0.16.0
Windows 11 + x86_64 + wsl2 + ubuntu24.04 + zsh

識別子の命名規則

変数名のみならず、型名や関数名などプログラムのあらゆる名前を識別子という。zigの識別子は以下の命名規則に従う。

  • 1文字目:英字([A-Za-z])か”_”(アンダーバー)
  • 2文字目以降:英数字([A-Za-z0-9])か”_”(アンダーバー)

また、Cライブラリなどでどうしても命名規則に従えない場合に以下で囲むことにより冒頭が数字、記号やスペースを含む、日本語等、何でもエスケープが可能である。

  • 冒頭:@”
  • 末尾:”

例:@”123exists”、@”w/ spaces in it”、@”日本語”

型については、公式ドキュメントの型一覧表にCとの対応まで併せて記載されており、一番分かり易いため、本稿ではかいつまんで説明する。

整数

整数の型は、符号あり(signed)整数を示すiか、符号なし(unsigned)整数を示すuの後に任意のbit数を続けることで記述される。任意というのはそのままの意味で、最大65535bitまで指定することが可能である。きっとbit数を格納する領域が2byteなのだろう。

例:i32u8u65535、等

Cの型、正確にはCのABI互換の型も用意されており、Cの型を示すc_の後にCの型を彷彿とさせるような名前が続く。

例:c_charc_shortc_uint、等

浮動小数点数

浮動小数点数の型も整数と同様な記述だが、浮動小数点数を示すfの後に続くことができるのは特定のbit数のみである。

例:f16f32f64f80f128

ポインタサイズ

zigには対象変数のポインタサイズに合わせる整数型も用意されており、符号無しのみではく、相対位置の計算に使えるように符号有りの二種類が用意されている。

例:usizeisize

comptime

zigの特筆すべき機能の一つであるcomptime(コンパイル時にコードを実行し評価しておくことで、実際の実行時には計算不要となり結果のみ使用する機能)で評価される値のうち、整数、浮動小数点数、型については専用の型を持つ。型を明示せず初期値を格納する宣言では、comptime_intかcomptime_floatが適用されるが、それはcomptimeかconst宣言にしか許されない。

例:comptime_intcomptime_floattype

その他

その他にも、値にtruefalseをとるbool型や、値にvoid{}をとるvoid型、値にエラーコードをとるanyerror型、値にreturnbreakcontinue、等をとるnoreturn型、等が用意されている。

error union

型の頭に!を付けると、対象の型か、エラーコードを扱うことが可能になる。!の前に何も記述が無い場合は、anyerror!と同義であるが、この構文は関数の戻り値型でのみ有効である。error{}でエラーセットを定義し、!の前に記述することで、扱うエラーセットを限定することも可能である。

例:!voidmyerror!i32 ※、等
  ※:const myerror = error{hoge, fuga};

optional

型の頭に?を付けると、対象の型か、nullを扱うことが可能になる。尚、optionalでなくても、undefined(未初期化メモリ)は代入可能である。build modeと呼ばれる4種類の最適化でsafety checksの列がonとなっているbuild modeでは、undefinedのままの変数を読み取ろうとするとエラーを吐くことができる。

例:?i32?f64、等

宣言

定数と変数はそれぞれ以下のように宣言する。

  • 定数:const 識別子: 型 = 値;
  • 変数:var 識別子: 型 = 値;

型を明示せず初期値を格納する宣言では、comptime_intかcomptime_floatが適用されるが、var宣言では以下のerrorとnoteが出力される。

  • error: variable of type ‘comptime_int’ must be const or comptime
  • note: to modify this variable at runtime, it must be given an explicit fixed-size number type

また、そもそも初期化をしないと以下のerrorが出力される。初期化したくない場合は、明示的にundefinedを代入すること。

  • error: expected ‘=’, found ‘;’

constとvar

constはconstant(定数)、varはvariable(変数)の略だと推定され、varは何回も代入できるのに対し、constは名前を付けているイメージに近く関数やzigでいうコンテナなども対象だが、一回しか代入することはできない。その特性故、constとvarでは最適化の効き方が異なる。
また、varで宣言したにも関わらず一度も値を変更しない場合は、ビルド時に以下のerrorとnoteが出力される。

  • error: local variable is never mutated
  • note: consider using ‘const’

尚、この場合でも実行ファイルは生成されているが、constに修正した場合と比較してファイルサイズが小さく、実際に実行はできない(exec format errorが出力される)ことから、生成途中のものが残っていると推測する。

更に、そもそも一度も使用していない定数・変数が存在する場合はビルド時に以下のerrorが出力される。

  • error: unused local constant
  • error: unused local variable

この場合、代入先を”_”(アンダーバー)にすることで、不要な定数・変数を宣言することなく値を捨てることができ、errorも回避できる。

型の確認方法

型は@TypeOf()で確認することができる。

zig: verification.zig

const std = @import("std");
const print = std.debug.print;

pub fn main(_: std.process.Init) !void {
    const a = 256;
    var x: u9 = undefined;
    x = a;
    const b: u8 = 255;
    print("a: {},type: {}\n", .{ a, @TypeOf(a) });
    print("x: {},type: {}\n", .{ x, @TypeOf(x) });
    print("b: {},type: {}\n", .{ b, @TypeOf(b) });
}

zsh

% zig build-exe -fno-llvm -O ReleaseSafe verification.zig
% ./verification
a: 256,type: comptime_int
x: 256,type: u9
b: 255,type: u8

今回はここまでとするにゅんちゅ。サンプルコードと出力結果も掲載したくて色々考えたが、コードの内容を説明してからと思うと最後にまとめて出すしかなくなり、それは諦めて途中途中に挟むとそれはそれで説明が分断され過ぎて読みにくいと感じ、このような形に行き着いた。もっと分かり易いまとめ方があると感じるが、いつまでも燻って進められない方が悪いと考え一旦次に進みつつも、引き続きまとめ方も勉強し精進する所存である。