mcpField
Z
zig-0.15

zig-0.15

github.com/zigcc開発5293 回インストール2 週間前に更新
リポジトリ

zig-0.15 について

Zig 0.15.xを使う開発者向けの、0.15.2に固定されたプログラミングガイドです。標準ライブラリとビルドシステムの主なAPI変更を、移行例とともに説明します。

主な機能

  1. 01Zig 0.15のAPI変更点
  2. 02std.Io.ReaderとWriterの使い方
  3. 03標準ライブラリAPIの例
  4. 04build.zigの移行ガイド
  5. 05よくあるエラーと修正方法

活用シーン

  1. 01旧コードをZig 0.15へ移行
  2. 02ArrayListとHashMapの使い方を確認
  3. 03I/O、HTTP、暗号コードを更新
  4. 04ビルドスクリプトやコンパイルエラーを調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zigcc/skills --skill zig-0.15

インストール後、Claude Code で/zig-0.15を入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

VERSIONING.md
README.md
scripts/
check-zig-version.sh

詳細

Zig 0.15.x プログラミングガイド

対象バージョン:このスキルは Zig 0.15.x(具体的には 0.15.2)に固定されています。 リリースノート:https://ziglang.org/download/0.15.1/release-notes.html

ローカルドキュメントを優先

zig env を実行してインストールパスを確認してください。パスをハードコードしないでください。主なフィールド:.lib_dir、.std_dir、.version。

  • 言語リファレンス:<lib_dir>/../doc/langref.html
  • 標準ライブラリのソース:API を確認するには、.std_dir 配下のファイルを読んでください。

ウェブ検索の前に、必ずローカルドキュメントを確認してください。

Zig 0.15 の重要な API 変更

ArrayList(破壊的変更)

すべての変更メソッドで、allocator パラメーターを明示的に指定する必要があります。

// ❌ WRONG (0.13 and earlier)
var list = std.ArrayList(T).init(allocator);
try list.append(item);
try list.appendSlice(items);
_ = try list.addOne();
_ = try list.toOwnedSlice();

// ✅ CORRECT (0.15+)
var list = try std.ArrayList(T).initCapacity(allocator, 16);
defer list.deinit(allocator);
try list.append(allocator, item);
try list.appendSlice(allocator, items);
_ = try list.addOne(allocator);
_ = try list.toOwnedSlice(allocator);

// AssumeCapacity variants do NOT need allocator
list.appendAssumeCapacity(item);
メソッド0.130.15+
appendtry list.append(item)try list.append(allocator, item)
appendSlicetry list.appendSlice(items)try list.appendSlice(allocator, items)
addOnetry list.addOne()try list.addOne(allocator)
ensureTotalCapacitytry list.ensureTotalCapacity(n)try list.ensureTotalCapacity(allocator, n)
toOwnedSlicetry list.toOwnedSlice()try list.toOwnedSlice(allocator)
deinitlist.deinit()list.deinit(allocator)
HashMap

Managed(アロケーターを内部に保持):

var map = std.StringHashMap(V).init(allocator);
defer map.deinit();
try map.put(key, value);  // No allocator needed

Unmanaged(操作ごとにアロケーターが必要):

var map = std.StringHashMapUnmanaged(V){};
defer map.deinit(allocator);
try map.put(allocator, key, value);  // Allocator required

Writergate:新しい std.Io.Writer と std.Io.Reader(大幅な再設計)

Zig 0.15 では、従来の汎用 std.io リーダー/ライターが、具象型で vtable ベースの std.Io.Reader/std.Io.Writer に置き換えられています。従来の API との主な違い:

  • 具象型(anytype ではありません)— 構造体に格納したり、パラメーターとして渡したりできます
  • 呼び出し元がバッファーを提供 — 隠れたアロケーションはありません
  • エラーが明確 — anyerror ではなく error{ReadFailed}/error{WriteFailed}
  • リングバッファー設計 — コピーせずに効率よく peek/take できます
std.Io.Writer
// Core struct (caller provides buffer, vtable handles draining)
pub const Writer = struct {
    vtable: *const VTable,
    buffer: []u8,       // caller-provided; buffer[0..end] = buffered data
    end: usize = 0,
};

主なメソッド:

メソッド用途
writeAll(bytes)すべてのバイトを書き込む(バッファリング後に排出する場合があります)
writeByte(byte)1 バイトを書き込む
print(fmt, args)書式付きで書き込む
flush()バッファー内のデータをすべて排出する
buffered()現在バッファーにあるデータのスライスを取得する

Writer の作成:

// 1. Fixed buffer (in-memory, bounded)
var buf: [256]u8 = undefined;
var writer: std.Io.Writer = .fixed(&buf);
try writer.print("count: {d}", .{42});
// written data: buf[0..writer.end]

// 2. Allocating (dynamic, growable)
var aw: std.Io.Writer.Allocating = .init(allocator);
defer aw.deinit();
try aw.writer.print("hello {s}", .{"world"});
const output = aw.written();           // []u8 view
const owned = try aw.toOwnedSlice();   // caller owns

// 3. From file
var file_buf: [4096]u8 = undefined;
var file_writer = file.writer(&file_buf);
try file_writer.interface.writeAll("data\n");
try file_writer.interface.flush();

// 4. stdout / stderr
var stdout_buf: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buf);
const stdout: *std.Io.Writer = &stdout_writer.interface;
try stdout.print("Hello\n", .{});
try stdout.flush();  // Don't forget!
std.Io.Reader
// Core struct (caller provides buffer, vtable handles filling)
pub const Reader = struct {
    vtable: *const VTable,
    buffer: []u8,       // caller-provided; buffer[seek..end] = buffered data
    seek: usize,        // bytes consumed
    end: usize,         // bytes available
};

主なメソッド:

メソッド用途
take(n)n バイトを正確に読み取り、位置を進める
takeByte()1 バイトを読み取る
takeInt(T, endian)エンディアンを指定して整数を読み取る
takeDelimiterExclusive(delim)区切り文字まで読み取る(区切り文字は含まない)
takeDelimiterInclusive(delim)区切り文字まで読み取る(区切り文字を含む)
peek(n)消費せずに次の n バイトを確認する
peekByte()1 バイトを先読みする
readSliceAll(buf)ストリームからバッファー全体を埋める
allocRemaining(gpa, limit)残りをすべて読み取り、確保したスライスに格納する
stream(writer, limit)データを Writer に送る
streamRemaining(writer)EOF までデータをすべてストリーム送信する
discard(limit)読まずにバイトをスキップする
buffered()現在バッファーにあるデータのスライスを取得する

Reader の作成:

// 1. Fixed buffer (in-memory, read-only)
var reader: std.Io.Reader = .fixed("line1\nline2\n");

// 2. From file
var read_buf: [4096]u8 = undefined;
var file_reader = file.reader(&read_buf);
const data = try file_reader.interface.take(10);

// 3. Limited (wrap existing reader with byte limit)
var limit_buf: [256]u8 = undefined;
var limited = reader.limited(.limited(100), &limit_buf);
// limited.interface reads at most 100 bytes

1 行ずつ読み取る:

while (reader.takeDelimiterExclusive('\n')) |line| {
    // process line (delimiter not included)
} else |err| switch (err) {
    error.EndOfStream => {},
    else => return err,
}

内容全体を確保済みのスライスに読み込む:

const contents = try reader.allocRemaining(allocator, .limited(1 << 20));
defer allocator.free(contents);
std.Io.Limit

ストリーム/読み取り操作で消費するバイト数を制御します:

const limit = std.Io.Limit.limited(1024);  // at most 1024 bytes
const unlimited = std.Io.Limit.unlimited;   // no limit
const nothing = std.Io.Limit.nothing;       // zero bytes
Reader と Writer:ストリーミング
// Pump all data from reader to writer
_ = try reader.streamRemaining(&writer);

// Pump with byte limit
_ = try reader.stream(&writer, .limited(4096));
File.Reader/File.Writer

std.fs.File はラッパー構造体を返します。std.Io.Reader/std.Io.Writer を取得するには、.interface フィールドを使用してください。

var write_buf: [4096]u8 = undefined;
var file_writer = file.writer(&write_buf);
try file_writer.interface.writeAll("hello\n");  // .interface is *std.Io.Writer
try file_writer.interface.flush();

var read_buf: [4096]u8 = undefined;
var file_reader = file.reader(&read_buf);
const line = try file_reader.interface.takeDelimiterExclusive('\n');

モードは 2 種類あります:file.writer(buf)(位置指定、スレッドセーフ)と file.writerStreaming(buf)(逐次処理)です。利用可能な場合は、位置指定モードを優先してください。

旧 std.io からの移行
旧版(0.14)新版(0.15)
std.io.getStdOut().writer()std.fs.File.stdout().writer(&buf) → .interface
std.io.fixedBufferStream(&buf)std.Io.Writer.fixed(&buf) / std.Io.Reader.fixed(data)
fbs.getWritten()writer.buffered()
writer.writeAll(data)同じですが、*std.Io.Writer に対して使用します
GenericWriter / AnyWriter*std.Io.Writer(具象型)
GenericReader / AnyReader*std.Io.Reader(具象型)
reader.readAll(buf)reader.readSliceAll(buf)
reader.readUntilDelimiter(delim, buf)reader.takeDelimiterExclusive(delim)

ブリッジアダプター — 旧形式の GenericWriter が残っていて、新しいインターフェースが必要な場合:

fn useOldWriter(old_writer: anytype) !void {
    var adapter = old_writer.adaptToNewApi(&.{});
    const w: *std.Io.Writer = &adapter.new_interface;
    try w.print("{s}", .{"example"});
}
HTTP クライアント(大幅な書き換え)
// High-level fetch()
var client: std.http.Client = .{ .allocator = allocator };
defer client.deinit();
const result = try client.fetch(.{
    .location = .{ .url = "https://example.com/api" },
    .method = .GET,
});

// Low-level request/response
const uri = try std.Uri.parse("https://example.com/api");
var req = try client.request(.POST, uri, .{
    .extra_headers = &.{
        .{ .name = "Content-Type", .value = "application/json" },
    },
});
defer req.deinit();
try req.sendBodyComplete(@constCast("{\"key\": \"value\"}"));

var redirect_buf: [4096]u8 = undefined;
var response = try req.receiveHead(&redirect_buf);
// Read body via std.Io.Reader
var body_reader = response.reader(&redirect_buf);
const body = try body_reader.allocRemaining(allocator, .limited(1 << 20));
defer allocator.free(body);
Base64
// ❌ WRONG (0.13) — std.base64.standard.encode / .decode
// ✅ CORRECT (0.15+) — use .Encoder / .Decoder sub-namespace

// Encode
const encoded = std.base64.standard.Encoder.encode(&buf, data);

// Decode
const decoded = try std.base64.standard.Decoder.decode(&out_buf, encoded);

// URL-safe variant: std.base64.url_safe.Encoder / .Decoder
Ed25519 暗号化(大幅な変更)

キー型は生のバイト配列ではなく、構造体になりました:

const Ed25519 = std.crypto.sign.Ed25519;

// ❌ WRONG (0.13 - SecretKey was [32]u8)
const secret: [32]u8 = ...;
const kp = Ed25519.KeyPair.fromSecretKey(secret);

// ✅ CORRECT (0.15+ - SecretKey is struct with 64 bytes)
// From 32-byte seed (deterministic):
const seed: [32]u8 = ...;
const kp = try Ed25519.KeyPair.generateDeterministic(seed);

// From 64-byte secret key:
var secret_bytes: [64]u8 = ...;
const secret_key = try Ed25519.SecretKey.fromBytes(secret_bytes);
const kp = try Ed25519.KeyPair.fromSecretKey(secret_key);

// Get public key bytes:
const pubkey_bytes: [32]u8 = kp.public_key.toBytes();

// Get seed:
const seed: [32]u8 = kp.secret_key.seed();

署名の変更:

// ❌ WRONG (0.13 - fromBytes returned error union)
const sig = try Ed25519.Signature.fromBytes(bytes);

// ✅ CORRECT (0.15+ - fromBytes does NOT return error)
const sig = Ed25519.Signature.fromBytes(bytes);
型イントロスペクションの列挙型(大文字・小文字の変更)
// ❌ WRONG (0.13 - PascalCase)
if (@typeInfo(T) == .Slice) { ... }
if (@typeInfo(T) == .Pointer) { ... }
if (@typeInfo(T) == .Struct) { ... }

// ✅ CORRECT (0.15+ - lowercase/escaped)
if (@typeInfo(T) == .slice) { ... }
if (@typeInfo(T) == .pointer) { ... }
if (@typeInfo(T) == .@"struct") { ... }
if (@typeInfo(T) == .@"enum") { ... }
if (@typeInfo(T) == .@"union") { ... }
if (@typeInfo(T) == .array) { ... }
if (@typeInfo(T) == .optional) { ... }
カスタムフォーマット関数
// ❌ WRONG (0.13 - used {} format specifier)
pub fn format(
    self: Self,
    comptime fmt: []const u8,
    options: std.fmt.FormatOptions,
    writer: anytype,
) !void {
    _ = fmt;
    _ = options;
    try writer.writeAll("...");
}
// Usage: std.fmt.bufPrint(&buf, "{}", .{value});

// ✅ CORRECT (0.15+ - use {f} format specifier)
pub fn format(self: Self, writer: anytype) !void {
    _ = self;
    try writer.writeAll("...");
}
// Usage: std.fmt.bufPrint(&buf, "{f}", .{value});
JSON の解析とシリアライズ
const MyStruct = struct {
    name: []const u8,
    value: u32,
};

// ✅ CORRECT: Parsing (0.15+)
const json_str =
    \\{"name": "test", "value": 42}
;
const parsed = try std.json.parseFromSlice(MyStruct, allocator, json_str, .{});
defer parsed.deinit();
const data = parsed.value;

// ✅ CORRECT: Serialization (0.15.2 - writer-based API)
// Note: There is NO stringifyAlloc in 0.15.2!

// Method 1: Using std.json.Stringify with Allocating writer
var out: std.Io.Writer.Allocating = .init(allocator);
defer out.deinit();
var stringify: std.json.Stringify = .{
    .writer = &out.writer,
    .options = .{},
};
try stringify.write(data);
const json_output = out.written();

// Method 2: Using std.fmt with json.fmt wrapper
const formatted = try std.fmt.allocPrint(allocator, "{f}", .{std.json.fmt(data, .{})});
defer allocator.free(formatted);

// Method 3: To fixed buffer
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
var writer = fbs.writer();
var stringify2: std.json.Stringify = .{
    .writer = &writer.adaptToNewApi(&.{}).new_interface,
    .options = .{},
};
try stringify2.write(data);
const output = fbs.getWritten();

// Parse options
const parsed2 = try std.json.parseFromSlice(MyStruct, allocator, json_str, .{
    .allocate = .alloc_always,
    .ignore_unknown_fields = true,
    .max_value_len = 1 << 20,
});

よくあるエラーメッセージと修正方法

エラー原因修正方法
expected 2 argument(s), found 1ArrayList メソッドにアロケーターが指定されていません最初の引数としてアロケーターを追加します
no field or member function named 'encode'std.base64.standard.encode を使用していますstd.base64.standard.Encoder.encode を使用します
no field or member function named 'open'古い HTTP API を使用していますclient.request() または client.fetch() を使用します
expected type 'SecretKey', found '[32]u8'Ed25519 の SecretKey が構造体になりましたgenerateDeterministic(seed) を使用します
expected error union type, found 'Signature'Signature.fromBytes はエラーを返しませんtry を削除します
enum has no member named 'Slice'@typeInfo の列挙型ケースが変更されました小文字の .slice を使用します
no field named 'root_source_file'古い build.zig API ですroot_module = b.createModule(...) を使用します

ビルドシステム(build.zig)の変更(大幅な書き換え)

公式ガイド:https://ziglang.org/learn/build-system/

実行ファイルの作成
// ❌ WRONG (0.14 and earlier)
const exe = b.addExecutable(.{
    .name = "hello",
    .root_source_file = .{ .path = "src/main.zig" },
    .target = target,
    .optimize = optimize,
});

// ✅ CORRECT (0.15+)
const exe = b.addExecutable(.{
    .name = "hello",
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    }),
});
ライブラリの作成
// ❌ WRONG (0.14 - addSharedLibrary/addStaticLibrary)
const lib = b.addSharedLibrary(.{
    .name = "mylib",
    .root_source_file = .{ .path = "src/lib.zig" },
});

// ✅ CORRECT (0.15+ - unified addLibrary with linkage)
const lib = b.addLibrary(.{
    .name = "mylib",
    .linkage = .dynamic,  // or .static
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    }),
});
パスの処理
// ❌ WRONG (0.14 - .path field)
.root_source_file = .{ .path = "src/main.zig" },

// ✅ CORRECT (0.15+ - use b.path())
.root_source_file = b.path("src/main.zig"),
モジュールの依存関係
// ❌ WRONG (0.14)
exe.addModule("sdk", sdk_module);

// ✅ CORRECT (0.15+)
exe.root_module.addImport("sdk", sdk_module);
テスト
// ✅ CORRECT (0.15+)
const unit_tests = b.addTest(.{
    .root_module = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = b.graph.host,  // use b.graph.host for native target
    }),
});

const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_unit_tests.step);
build.zig の完全な例
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "hello",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    b.installArtifact(exe);

    const run_exe = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run the application");
    run_step.dependOn(&run_exe.step);
}

言語の変更

usingnamespace の削除

usingnamespace キーワードは Zig 0.15 で完全に削除されました。

// ❌ WRONG (removed in 0.15)
pub usingnamespace @import("other.zig");

// ✅ CORRECT - explicit re-exports
pub const foo = @import("other.zig").foo;
pub const bar = @import("other.zig").bar;

// ✅ CORRECT - namespace via field
pub const other = @import("other.zig");
// Usage: other.foo, other.bar

ミックスインの移行:@fieldParentPtr とゼロビットフィールドを使用します:

// ❌ OLD mixin pattern
pub const Foo = struct {
    count: u32 = 0,
    pub usingnamespace CounterMixin(Foo);
};

// ✅ NEW mixin pattern (0.15+)
pub fn CounterMixin(comptime T: type) type {
    return struct {
        pub fn increment(m: *@This()) void {
            const x: *T = @alignCast(@fieldParentPtr("counter", m));
            x.count += 1;
        }
    };
}

pub const Foo = struct {
    count: u32 = 0,
    counter: CounterMixin(Foo) = .{},  // zero-bit field
};
// Usage: foo.counter.increment()
async/await キーワードの削除

async、await、および @frameSize は削除されました。非同期機能は、標準ライブラリの新しい I/O インターフェースを通じて提供される予定です。

// ❌ REMOVED - no async/await keywords
async fn fetchData() ![]u8 { ... }
const result = await fetchData();

// ✅ Use std.Io interfaces or threads instead
undefined に対する算術演算

不正な動作を引き起こす可能性のある undefined に対する演算は、コンパイルエラーになります:

const a: u32 = 0;
const b: u32 = undefined;

// ❌ COMPILE ERROR in 0.15+
_ = a + b;  // error: use of undefined value here causes illegal behavior
整数から浮動小数点数への非可逆な型変換

整数を正確に表現できない場合、コンパイルエラーになります:

// ❌ COMPILE ERROR in 0.15+
const val: f32 = 123_456_789;  // error: cannot represent precisely

// ✅ CORRECT - opt-in to floating-point rounding
const val: f32 = 123_456_789.0;

コメント