zig-0-17 について
zig-0-17は、Zig 0.17.0を扱う開発者向けのプログラミングガイドです。このバージョン固有の言語と標準ライブラリの変更を、移行例とともに説明し、ローカル環境のドキュメントやソースを先に確認する方法も示します。
主な機能
- 01Zig 0.17.0の変更リファレンス
- 02破壊的変更の移行例
- 03組み込み機能と構文の更新
- 04標準ライブラリAPIの変更
- 05ローカルドキュメント優先の方針
活用シーン
- 01Zig 0.16.xコードの移行
- 02更新された組み込み機能の確認
- 03標準ライブラリ呼び出しの修正
- 04ローカル文書に基づくAPI確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zigcc/ skills --skill zig-0-17インストール後、Claude Code で/zig-0-17を入力して読み込まれたか確認します。
同梱ファイルscripts/check-zig-version.sh
詳細
Zig 0.17.0 プログラミングガイド
バージョンの対象範囲:Zig 0.17.0(安定版)に固定されています。 リリースノート:https://ziglang.org/download/0.17.0/release-notes.html
Zig 0.17.0 では、言語の安定性が 1.0 に向けてさらに高められています。主な変更点には、@intFromEnum/@enumFromInt を @backingInt/@fromBackingInt に置き換えたこと、型リフレクション(std.lang.Type)とコンテナーの組み込み機能を構造体配列(SoA)形式に移行したこと、配列の乗算構文 ** を削除して @splat に置き換えたこと、DebugAllocator をスレッドセーフな SafeAllocator に置き換えたこと、fmt.allocPrint を mem.Allocator に移したこと、C 変換を外部パッケージに分離したこと、ビルドシステムを Configurer と Maker の独立したプロセスに分割し、細粒度のキャッシュ汚染追跡を導入したことなどがあります。
まずローカルドキュメントを確認
zig env を実行してローカルのインストールパスを確認してください。パスをハードコードしないでください。
- 言語リファレンス:
<lib_dir>/../doc/langref.html - 標準ライブラリのソース:正確なシグネチャは
.std_dir以下のソースファイルを確認してください。 - 標準ライブラリのドキュメント:
zig stdを実行すると、ローカルのドキュメント HTTP サーバーが起動します。
オンラインで検索する前に、必ずローカルのドキュメントまたはソースを確認してください。
クイックリファレンス:主な破壊的変更
| 旧(0.16.x) | 新(0.17.0) | 備考 |
|---|---|---|
@intFromEnum(val) | @backingInt(val) | ビットパックおよびタグ付きユニオンでも使用可能 |
@enumFromInt(val) | @fromBackingInt(val) | 戻り値の型は推論されます。不正な値は安全性チェックの対象です |
[1]T{val} ** len | @splat(val) | 配列の乗算 ** は削除されました。@splat を使用してください |
@bitCast(extern_struct_val) | @ptrCast(&val).* または extern union | @bitCast は extern struct/extern union に対して使用できません |
void{} | {} | 構文 void{} は削除されました |
errdefer |err| { ... } | ヘルパー関数に切り出し、catch |err| を使用 | errdefer でのエラーキャプチャは削除されました |
i0 | u0 | プリミティブ整数型 i0 は削除されました |
@hasDecl(同じファイル内の非公開宣言にも true を返す) | pub 宣言に対してのみ true を返す | ファイル間で動作が統一されました |
@cImport({...}) | b.dependency("translate_c", .{}) + Translator | @cImport は削除されました。C トランスレーターはパッケージに移されました |
std.heap.DebugAllocator | std.heap.SafeAllocator | スレッドセーフで、再利用せず、リークと破損を検出します |
std.heap.StackFallbackAllocator(N) | std.heap.BufferFirstAllocator.init(buf, fallback) | comptime サイズではなく、バッファーを明示的に指定します |
std.fmt.allocPrint(arena, ...) | arena.print(...)/allocator.print(...) | std.mem.Allocator に移されました |
list.getLastOrNull() | list.last() | ?T を返します |
list.getLast() | list.last().? | .? で非 null をアサートします。新しい list.lastPtr() は ?*T を返します |
std.zon.parse.fromSliceAlloc | std.zon.parse.fromSlice(T, .{ ... }) | 構造体引数を使用します。新しく updateFromSlice が追加されました |
std.bit_set.IntegerBitSet | std.bit_set.Integer | 同様に Array、Static、Dynamic も変更されました。.empty/.full |
@typeInfo(S).@"struct".fields | field_names、field_types、field_attrs | 型リフレクションは構造体配列(SoA)形式に書き換えられました |
@Struct(引数 7 個) | @Struct(layout, BackingInt, names, types, attrs) | 引数は 5 個です。@Union は 5 個、@Enum は 4 個です |
std.lang.OptimizeMode | std.lang.Optimize | タグは小文字になりました:.debug、.safe、.fast、.small |
@import("builtin").cpu / os / abi | @import("builtin").target.cpu / os / abi | .target 経由でアクセスします |
b.build_root(Directory) | b.root(Cache.Path) | パスの表現が変更されました |
if (b.args) |args| run.addArgs(args); | run.addPassthruArgs() | 引数の変更があっても configure キャッシュを純粋に保ちます |
b.findProgram(...) | b.findProgramLazy(...) | 遅延解決により configure キャッシュの汚染を防ぎます |
言語の変更点
1. @backingInt と @fromBackingInt による enum 変換ビルトインの置き換え
@intFromEnum と @enumFromInt は非推奨になりました(#35966)。代わりに @backingInt と @fromBackingInt を使用してください。
@backingIntは次の型で使用できます。- すべての
enum型。 - 明示的なバッキング整数を持つビットパック(
packed struct/packed union)。 - タグ付き
union型(アクティブなタグのバッキング整数を返します)。
- すべての
@fromBackingIntは変換先の型を推論します(任意の enum、または明示的なバッキング整数を持つビットパック)。バッキング型に一致する整数を受け取り、enum のタグ値を安全性チェック付きで検証します。- 空の enum では、バッキング整数として
noreturnを宣言する必要があります(例:enum(noreturn) {})。 zig fmtは古い@intFromEnumおよび@enumFromIntの呼び出しを自動的に更新します。
const Status = enum(u8) {
ok = 0,
err = 1,
};
const status: Status = .ok;
// Convert enum to backing integer
const raw: u8 = @backingInt(status);
// Convert backing integer to enum (type is inferred from context)
const restored: Status = @fromBackingInt(raw);
// Helper function to query backing integer type:
const TagInt = std.meta.BackingInt(Status); // u8
2. @bitCast のセマンティクス:論理ビット表現のみ
Zig 0.17.0 では、@bitCast が値の論理ビット表現を再解釈するように再定義され、エンディアンに完全に依存しなくなりました。
- 使用できる型:
void、bool、整数(comptime_intを除く)、浮動小数点数(comptime_floatを除く)、整数をバッキング型とするenum(T)、packed struct(T)、packed union(T)、およびそれらの配列/ベクトル。 - 禁止:
extern structとextern unionは@bitCastに渡せなくなりました。 extern structのメモリ表現を型パニングするには、@ptrCastまたはextern unionを使用してください。
const TwoBytes = extern struct {
b0: u8,
b1: u8,
};
test "type pun extern struct in 0.17" {
const bytes: TwoBytes = .{ .b0 = 0x12, .b1 = 0xAB };
// OLD (0.16): const int: u16 = @bitCast(bytes); // COMPILE ERROR in 0.17
// NEW (0.17): Use pointer cast
const int_ptr: *align(1) const u16 = @ptrCast(&bytes);
const int: u16 = int_ptr.*;
_ = int;
}
3. 配列の乗算 ** を削除 — @splat を使用
繰り返し構文 [1]u8{0} ** N は削除されました。代わりに @splat を使用してください。
// OLD (0.16)
var buf = [1]u8{0} ** 1024;
const arr = [1]f32{1.5} ** 4;
// NEW (0.17)
var buf: [1024]u8 = @splat(0);
const arr: [4]f32 = @splat(1.5);
4. @divCeil を追加
整数除算を正の無限大($+\infty$)方向に丸めます。@divTrunc、@divFloor、@divExact を補完するものです。
try std.testing.expectEqual(2, @divCeil(5, 3));
try std.testing.expectEqual(-1, @divCeil(-5, 3));
5. @hasDecl は公開(pub)宣言のみを調べる
以前は、同じソースファイル内から呼び出した場合、@hasDecl(T, name) は非公開の宣言に対しても true を返していました。現在は呼び出し元のコンテキストにかかわらず、pub 宣言のみを厳密にチェックします。
const Foo = struct {
pub const public_val = 1;
const private_val = 2;
};
// In 0.17:
@hasDecl(Foo, "public_val") == true;
@hasDecl(Foo, "private_val") == false; // Always false even within the same file
6. コンパイル時に長さが決まるスライスの間接参照と配列への型強制
長さがコンパイル時に決まるスライスを、直接間接参照したり、配列ポインターに型強制したりできるようになりました。
const slice: []const u16 = &.{ 1, 2, 3 };
const array: [3]u16 = slice.*; // Valid in 0.17
const array_ptr: *const [3]u16 = slice; // Valid in 0.17
7. 構文の削除:void{}、errdefer |err|、i0
void{}を削除:void{}を{}に置き換えてください。errdefer |err|を削除:errdeferでエラーを捕捉できなくなりました。内部関数に切り分けるか、catch |err|で処理してください。// OLD: // errdefer |err| std.log.err("failed: {s}", .{@errorName(err)}); // NEW: fn run() void { runInner() catch |err| { std.log.err("failed: {s}", .{@errorName(err)}); }; } fn runInner() !void { ... }i0を削除:i0の使用箇所はすべてu0に置き換えてください。- グローバルリンケージの整理:
std.lang.GlobalLinkageのinternalとlink_onceが削除されました。link_onceにはweakを使用し、internalの代わりに@exportを省略してください。
8. @cImport を削除 — 外部 translate_c パッケージ
@cImport は完全に削除されました。また、std.Build.Step.TranslateC は非推奨となり、公式の外部パッケージが推奨されるようになりました。
zig fetch --save git+https://codeberg.org/ziglang/translate-c
build.zig では次のようにします。
const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const translate_c_dep = b.dependency("translate_c", .{});
const translator: Translator = .init(translate_c_dep, .{
.c_source_file = b.path("src/c.h"),
.target = target,
.optimize = optimize,
});
translator.linkSystemLibrary("glfw3", .{});
const exe = b.addExecutable(.{
.name = "myapp",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "c", .module = translator.mod },
},
}),
});
b.installArtifact(exe);
}
標準ライブラリの変更
1. std.heap.SafeAllocator(DebugAllocator に代わるもの)
std.heap.DebugAllocator は非推奨になりました。代わりに std.heap.SafeAllocator を使用してください。
- スレッドセーフ:外部ロックなしで並行処理できます。
- 安全性の保証:
deinit()時にリークが報告されます。不正な解放、操作の競合、バッファーの上書きは、AllocFooterのチェックサムによってパニックまたはセグメンテーションフォールトを引き起こします。 - メモリを再利用しない:解放済みメモリを再利用せず、解放後使用バグを検出します。
const std = @import("std");
test "SafeAllocator usage" {
var gpa: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
defer {
const leaks = gpa.deinit();
if (leaks != 0) @panic("Memory leak detected!");
}
const allocator = gpa.allocator();
const slice = try allocator.alloc(u32, 16);
defer allocator.free(slice);
}
2. BufferFirstAllocator(再構成された StackFallbackAllocator)
StackFallbackAllocator(N) は std.heap.BufferFirstAllocator に置き換えられました。コンパイル時のバッファーサイズを宣言するのではなく、明示的なバッファースライスを渡してください。
var stack_buf: [256]u8 = undefined;
var bfa: std.heap.BufferFirstAllocator = .init(&stack_buf, gpa);
const allocator = bfa.allocator();
// Allocations small enough will use stack_buf; spills fallback to gpa.
3. std.fmt.allocPrint に代わる mem.Allocator.print
allocPrint は std.mem.Allocator のインスタンスメソッドになりました。
// OLD (0.16)
const str = try std.fmt.allocPrint(allocator, "{s}={d}", .{ key, val });
// NEW (0.17)
const str = try allocator.print("{s}={d}", .{ key, val });
// For null-terminated strings:
const zstr = try allocator.printSentinel("{s}={d}", .{ key, val }, 0);
4. ArrayList の更新:last()、last().?、lastPtr()
getLastOrNull()は非推奨です $\rightarrow$last()(?Tを返します)を使用してください。getLast()は非推奨です $\rightarrow$last().?を使用してください。lastPtr()を追加 $\rightarrow$?*Tを返します。
var list = try std.ArrayList(u32).initCapacity(allocator, 8);
defer list.deinit(allocator);
try list.append(allocator, 42);
if (list.last()) |val| {
// val == 42
}
const non_null = list.last().?;
if (list.lastPtr()) |ptr| {
ptr.* += 1;
}
5. std.zon.parse の再設計
std.zon は設定構造体を受け取り、アリーナからメモリを確保するようになりました。
const Config = struct {
host: []const u8,
port: u16,
};
var diag: std.zon.parse.Diagnostics = undefined;
const parsed = try std.zon.parse.fromSlice(Config, .{
.gpa = gpa,
.arena = arena,
.source = zon_text,
.diagnostics = &diag,
});
// For in-place field updates:
// std.zon.parse.updateFromSlice(&config, .{ ... });
6. std.bit_set の名称変更
標準ビットセットのバリアントが最新化されました。
IntegerBitSet$\rightarrow$std.bit_set.IntegerArrayBitSet$\rightarrow$std.bit_set.ArrayStaticBitSet$\rightarrow$std.bit_set.StaticDynamicBitSetUnmanaged$\rightarrow$std.bit_set.DynamicDynamicBitSet(管理型)$\rightarrow$std.bit_set.DynamicManaged(非推奨)initEmpty()/initFull()$\rightarrow$.empty/.full宣言リテラルを使用します。
7. 型リフレクション:std.lang.Type の構造体配列(SoA)
std.lang.Type の型検査フィールドが、構造体の配列(fields: []Field)から配列構造体(field_names、field_types、field_attrs)に移行しました。
fn printFieldNames(comptime T: type) void {
const info = @typeInfo(T).@"struct";
inline for (info.field_names, info.field_types, info.field_attrs) |name, field_type, attrs| {
_ = field_type;
_ = attrs;
std.debug.print("Field: {s}\n", .{name});
}
}
型を作成する組み込み関数の更新:
@Struct(layout, BackingInt, field_names, field_types, field_attrs)(7 個から 5 個のパラメーターに変更)@Union(layout, tag_type, field_names, field_types, field_attrs)(5 個のパラメーター)@Enum(tag_type, mode, field_names, field_values)(4 個のパラメーター)
ヒント:フィールド属性のデフォルト値には &@splat(.{}) を使用します。
const MyStruct = @Struct(
.auto,
null,
&.{"x", "y"},
&.{i32, i32},
&@splat(.{}),
);
8. std.lang.Optimize(タグを小文字化)
std.lang.OptimizeMode の名称が std.lang.Optimize に変更されました。
- タグ:
.debug、.safe、.fast、.small std.debug.runtime_safetyの代わりにstd.lang.Optimize.runtimeSafety(mode)を使用します。
9. その他の非推奨化と整理
@import("builtin"):.cpu、.os、.abi、.object_formatは非推奨です $\rightarrow$.target.cpu、.target.os、.target.abi、.target.ofmtを使用します。std.builtinは非推奨となり、std.langが推奨されます。std.DoublyLinkedList.pop$\rightarrow$popLaststd.gpu$\rightarrow$std.spirvstd.ascii.indexOfIgnoreCase*$\rightarrow$std.ascii.findIgnoreCase*Uriはnet.HostNameから切り離されました $\rightarrow$std.Io.net.HostName.fromUri(uri, &buf)を使用します。mem.eqlとmem.findDiffは、float スライスのポインターが等しい場合に早期終了しなくなりました(NaNを正しく処理します)。
ビルドシステムとツールチェーン
1. Maker プロセスと Configurer プロセスの分離
zig build はプロジェクトの構成とビルドの実行を分離します。
- Configurer:
build.zigを評価し、ビルドグラフを表すバイナリ形式に変換します。 - Maker:タスクの評価、パッケージの取得、ステップの実行を担う、再利用可能なプリコンパイル済みバイナリです。
- 後続の実行を高速化し、IDE やツール向けの Build Server Protocol (
--listen=-) を支えます。
2. 構成キャッシュのポイズニング
build.zig のロジックが、キャッシュシステムで追跡できない副作用を生むと、構成キャッシュがポイズニングされ、次回の実行時に全面的な再評価が強制されます。
b.findProgram(...)は構成時に即座に PATH を検索します $\rightarrow$ キャッシュをポイズニングします。- 推奨:
b.findProgramLazy(...)はLazyPathを返します $\rightarrow$ 純粋で、キャッシュをポイズニングしません。 - ファイル/ディレクトリの依存関係を明示的に宣言してキャッシュの純粋性を保つには:
b.dependOnFileContents(path); b.dependOnFileMetadata(path); b.dependOnDirectoryContents(dir_path); b.dependOnDirectoryMetadata(dir_path); - CLI フラグで上書きします:
--cache-poison=pure(デフォルト)、poisoned、disallowed、またはignored。
3. Run ステップ:Passthru 引数
build.zig で b.args を調べないでください。addPassthruArgs() を使用します。
// OLD (0.16)
if (b.args) |args| {
run_cmd.addArgs(args);
}
// NEW (0.17)
run_cmd.addPassthruArgs();
zig build run -- arg1 arg2 で CLI 引数を変更しても、構成キャッシュが無効にならなくなります!
4. システムライブラリと pkg-config
Zig 0.17 では、Module.linkSystemLibrary はデフォルトで pkg-config を使用します。
module.linkSystemLibrary("foo", .{});
.pc ファイルを含まない、Windows の bcrypt や ws2_32 などのプラットフォーム提供ライブラリでは、pkg-config を明示的に無効にします。
module.linkSystemLibrary("bcrypt", .{
.use_pkg_config = .no,
});
この場合も、ライブラリは名前でリンクされます。Zig が pkg-config を呼び出さないようにするだけです。
5. ビルドルートとパスのオプション
b.build_root(Directory) $\rightarrow$b.root(Cache.Path)。Step.Options:ファイルとディレクトリを区別します。addOptionPath(name, path)(ファイルのみ)addOptionPathDirectory(name, path)(ディレクトリのみ)addOptionPathUntracked(name, path)(依存関係の追跡対象から除外)
b.pathList(&.{ "src", "test" })は、fmtステップ用の[]const LazyPathを生成します。const fmt_step = b.addFmt(.{ .paths = b.pathList(&.{ "src", "build.zig" }), });b.dependencyLazyはerror.LazyDependencyNeededを返すことがあります。pub fn build(b: *std.Build) !voidでは、次のようにします。const dep = try b.dependencyLazy("my_dep", .{});
Zig 0.17.0 でよく使われるイディオム
標準的なプロジェクトのエントリーポイント(Juicy Main)
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const gpa = init.gpa;
const arena = init.arena.allocator();
// Print to stdout
try std.Io.File.stdout().writeStreamingAll(io, "Running on Zig 0.17.0!\n");
// Dynamic formatted string
const greeting = try gpa.print("Hello, {s}!", .{"World"});
defer gpa.free(greeting);
// Read CLI args
const args = try init.minimal.args.toSlice(arena);
for (args, 0..) |arg, i| {
std.log.info("arg[{d}]: {s}", .{ i, arg });
}
}
テストでの SafeAllocator によるメモリー管理
const std = @import("std");
test "data processing test" {
var gpa: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{});
defer {
const leaks = gpa.deinit();
std.debug.assert(leaks == 0);
}
const allocator = gpa.allocator();
var list = try std.ArrayList(u32).initCapacity(allocator, 4);
defer list.deinit(allocator);
try list.append(allocator, 100);
try std.testing.expectEqual(@as(u32, 100), list.last().?);
}
よくあるエラーメッセージと修正方法(0.17)
| エラーメッセージ | 根本原因 | 解決方法 |
|---|---|---|
error: cannot @bitCast from 'extern struct' | @bitCast は extern struct/extern union を受け付けません | @ptrCast(&val).* を使用するか、extern union を使用します |
error: expected 5 arguments, found 7 on @Struct | SoA の更新で @Struct のパラメーター数が減りました | デフォルト値/アラインメント/comptime を field_attrs スライスにまとめます |
error: no member named 'getLastOrNull' in 'ArrayList' | メソッド名が変更されました | list.last() を使用します |
error: no member named 'allocPrint' in 'std.fmt' | 関数が mem.Allocator に移動しました | allocator.print(...) を直接呼び出します |
error: expected type 'std.lang.Optimize', found 'std.builtin.OptimizeMode' | OptimizeMode の名前が変更され、タグが小文字になりました | std.lang.Optimize.debug/.safe/.fast/.small を使用します |
error: no member named 'initEmpty' in 'std.bit_set' | 初期化子の構文が更新されました | std.bit_set.Integer.empty または宣言リテラル .empty を使用します |
error: 'errdefer' cannot capture error | |err| キャプチャ構文が削除されました | ロジックをヘルパー関数にまとめ、catch |err| を使用します |
error: cannot multiply array types with '**' | 配列の乗算が削除されました | [1]T{val} ** N を @splat(val) に置き換えます |
移植方法(0.16 → 0.17)
- 最初に
zig fmtを実行します:zig fmtは@intFromEnum$\rightarrow$@backingIntと@enumFromInt$\rightarrow$@fromBackingIntを自動的に移行します。 - 配列の繰り返しを修正します:
**による配列の乗算を検索し、@splat(...)に置き換えます。 @bitCastを確認します:extern structまたはextern unionに@bitCastが使われていないことを確認します。ポインターへの@ptrCastに置き換えるか、extern unionにリファクタリングします。fmt.allocPrintを更新します:std.fmt.allocPrint(alloc, ...)をalloc.print(...)に変更します。ArrayListの呼び出しを更新します:getLastOrNull()をlast()に、getLast()をlast().?に置き換えます。- ビルドシステムを移行します:
build.zigで、run_cmd.addArgs(args)の条件分岐をrun_cmd.addPassthruArgs()に置き換えます。- パスが make 時にのみ必要な場合は、
b.findProgramをb.findProgramLazyに置き換えます。 b.build_rootをb.rootに置き換えます。b.pathList(...)を使って、フォーマット処理のステップで使用するpathsを更新します。- C 変換を使用する場合は、
build.zig.zonにtranslate_c依存関係を追加し、Translatorを使用します。
- テストを検証します:
zig build testを実行して正常にコンパイルできることを確認し、SafeAllocatorでメモリーリークがゼロであることを検証します。