第3章: 基本構文と変数

学習目標

この章を終えると、以下ができるようになります:

  • Zigの基本的な構文規則を理解する
  • 変数と定数の宣言方法を使い分けられる
  • Zigのプリミティブ型を理解し、適切に使用できる
  • 型推論と明示的な型指定を使い分けられる
  • 基本構文

    ソースファイルの構造

    Zigのソースファイルは、以下の要素で構成されます:

    // 1. インポート文
    const std = @import("std");
    
    // 2. 定数定義
    const VERSION = "1.0.0";
    const MAX_SIZE = 1024;
    
    // 3. 型定義
    const Point = struct {
        x: i32,
        y: i32,
    };
    
    // 4. 関数定義
    pub fn main() void {
        std.debug.print("Hello, Zig!\n", .{});
    }
    
    // 5. テスト
    test "basic test" {
        try std.testing.expectEqual(@as(i32, 4), 2 + 2);
    }
    

    コメント

    // これは単一行コメント
    
    /// これはドキュメントコメント
    /// 関数や型の説明に使用
    pub fn add(a: i32, b: i32) i32 {
        return a + b;
    }
    
    //! これはモジュールレベルのドキュメントコメント
    //! ファイルの先頭に書く
    

    Zigには複数行コメント(/ /)はありません。

    セミコロンとブロック

    // 文の終わりにはセミコロンが必要
    const x = 10;
    const y = 20;
    
    // ブロックは波括弧で囲む
    {
        const z = 30;
        // zはこのブロック内でのみ有効
    }
    
    // 式としてのブロック
    const result = blk: {
        const a = 10;
        const b = 20;
        break :blk a + b; // 30
    };
    

    変数と定数

    const vs var

    Zigには2種類の変数宣言があります:

    const std = @import("std");
    
    pub fn example() void {
        // const: 不変(変更不可)
        const x = 10;
        // x = 20; // コンパイルエラー!
    
        // var: 可変(変更可能)
        var y = 10;
        y = 20; // OK
    
        std.debug.print("x={}, y={}\n", .{x, y});
    }
    

    原則: デフォルトで const を使用し、必要な場合のみ var を使用します。

    変数の初期化

    // 明示的な型指定
    const x: i32 = 10;
    
    // 型推論
    const y = 10; // i32と推論される
    
    // 未初期化変数(undefined)
    var z: i32 = undefined;
    z = 100; // 後で初期化
    
    // 複数の変数宣言
    const a: i32 = 1;
    const b: i32 = 2;
    const c: i32 = 3;
    

    スコープとシャドーイング

    const std = @import("std");
    
    pub fn example() void {
        const x = 10;
    
        {
            const x = 20; // シャドーイング(外側のxを隠す)
            std.debug.print("Inner x: {}\n", .{x}); // 20
        }
    
        std.debug.print("Outer x: {}\n", .{x}); // 10
    }
    

    プリミティブ型

    整数型

    Zigは豊富な整数型を提供します:

    // 符号付き整数
    const i8_val: i8 = -128;      // 8ビット
    const i16_val: i16 = -32768;  // 16ビット
    const i32_val: i32 = -2147483648;  // 32ビット
    const i64_val: i64 = -9223372036854775808;  // 64ビット
    const i128_val: i128 = -170141183460469231731687303715884105728;  // 128ビット
    
    // 符号なし整数
    const u8_val: u8 = 255;       // 8ビット
    const u16_val: u16 = 65535;   // 16ビット
    const u32_val: u32 = 4294967295;  // 32ビット
    const u64_val: u64 = 18446744073709551615;  // 64ビット
    
    // 任意ビット幅の整数
    const u5_val: u5 = 31;        // 5ビット(0-31)
    const i3_val: i3 = -4;        // 3ビット(-4 to 3)
    
    // プラットフォーム依存の型
    const isize_val: isize = -100;  // ポインタサイズの符号付き整数
    const usize_val: usize = 100;   // ポインタサイズの符号なし整数
    

    浮動小数点型

    const f16_val: f16 = 3.14;    // 16ビット浮動小数点
    const f32_val: f32 = 3.14159; // 32ビット浮動小数点
    const f64_val: f64 = 3.141592653589793;  // 64ビット浮動小数点
    const f128_val: f128 = 3.141592653589793238462643383279502884;  // 128ビット浮動小数点
    
    // 浮動小数点リテラル
    const pi = 3.14159;  // デフォルトでf64と推論
    const e = 2.71828;
    
    // 指数表記
    const large = 1.23e10;  // 1.23 * 10^10
    const small = 1.23e-10; // 1.23 * 10^-10
    

    ブール型

    const is_true: bool = true;
    const is_false: bool = false;
    
    // 論理演算
    const and_result = true and false; // false
    const or_result = true or false;   // true
    const not_result = !true;          // false
    

    文字型

    // 文字(u8のエイリアス)
    const char: u8 = 'A';
    const newline: u8 = '\n';
    
    // UTF-8コードポイント(u21)
    const unicode_char: u21 = 'あ';
    const emoji: u21 = '😀';
    
    const std = @import("std");
    
    pub fn example() void {
        std.debug.print("char: {c}\n", .{char});
        std.debug.print("unicode: {u}\n", .{unicode_char});
    }
    

    void型

    // 値を返さない関数
    pub fn doSomething() void {
        // 何か処理
    }
    
    // voidリテラル(使用頻度は低い)
    const unit: void = {};
    

    型変換

    明示的な型変換

    Zigでは暗黙の型変換はありません。すべて明示的に行います:

    const std = @import("std");
    
    pub fn example() void {
        // 整数型の変換
        const x: i32 = 10;
        const y: i64 = @intCast(x); // i32 -> i64
    
        // 浮動小数点型の変換
        const f: f32 = 3.14;
        const d: f64 = @floatCast(f); // f32 -> f64
    
        // 整数から浮動小数点への変換
        const i: i32 = 42;
        const float_val: f64 = @floatFromInt(i);
    
        // 浮動小数点から整数への変換
        const float_num: f64 = 3.7;
        const int_val: i32 = @intFromFloat(float_num); // 3(切り捨て)
    
        std.debug.print("y={}, d={d:.2}, float_val={d:.1}, int_val={}\n",
            .{y, d, float_val, int_val});
    }
    

    ビットキャスト

    const std = @import("std");
    
    pub fn example() void {
        // ビットパターンを維持したまま型を変換
        const x: u32 = 0x3f800000;
        const f: f32 = @bitCast(x); // 1.0
    
        std.debug.print("Bit pattern 0x{x} as f32: {d:.1}\n", .{x, f});
    }
    

    数値リテラル

    整数リテラル

    // 10進数
    const decimal = 1234;
    
    // 16進数(0xプレフィックス)
    const hex = 0xFF; // 255
    
    // 8進数(0oプレフィックス)
    const octal = 0o755; // 493
    
    // 2進数(0bプレフィックス)
    const binary = 0b1111; // 15
    
    // アンダースコアで区切り(可読性向上)
    const large_number = 1_000_000;
    const hex_color = 0xFF_00_FF;
    const binary_mask = 0b1111_0000;
    

    浮動小数点リテラル

    // 通常の表記
    const normal = 3.14159;
    
    // 指数表記
    const scientific = 6.022e23;
    
    // 16進数浮動小数点
    const hex_float = 0x1.0p10; // 1.0 * 2^10 = 1024.0
    

    型推論とコンパイル時型

    型推論

    const std = @import("std");
    
    pub fn example() void {
        // 型推論
        const x = 10;        // i32と推論
        const y = 3.14;      // f64と推論
        const z = true;      // boolと推論
    
        // 型を取得
        const T1 = @TypeOf(x); // i32
        const T2 = @TypeOf(y); // f64
    
        std.debug.print("Type of x: {}\n", .{T1});
        std.debug.print("Type of y: {}\n", .{T2});
    }
    

    comptime型

    // コンパイル時にのみ存在する型
    const comptime_int = 42; // comptime_intとして推論
    const comptime_float = 3.14; // comptime_floatとして推論
    
    // 実行時の型に変換
    const runtime_int: i32 = comptime_int;
    const runtime_float: f64 = comptime_float;
    

    型エイリアス

    // 型に別名をつける
    const MyInt = i32;
    const MyFloat = f64;
    
    pub fn example() void {
        const x: MyInt = 10;
        const y: MyFloat = 3.14;
    }
    

    演算子

    算術演算子

    const std = @import("std");
    
    pub fn example() void {
        const a: i32 = 10;
        const b: i32 = 3;
    
        // 基本演算
        const add = a + b;      // 13
        const sub = a - b;      // 7
        const mul = a * b;      // 30
        const div = @divTrunc(a, b);  // 3(切り捨て除算)
        const mod = @mod(a, b); // 1(剰余)
    
        // ラップアラウンド演算(オーバーフロー時に折り返す)
        var x: u8 = 255;
        x +%= 1;  // 0
    
        // 飽和演算(オーバーフロー時に最大値/最小値)
        var y: u8 = 255;
        y +|= 1;  // 255
    
        std.debug.print("add={}, sub={}, mul={}, div={}, mod={}\n",
            .{add, sub, mul, div, mod});
    }
    

    比較演算子

    const std = @import("std");
    
    pub fn example() void {
        const a = 10;
        const b = 20;
    
        const eq = (a == b);  // false(等しい)
        const ne = (a != b);  // true(等しくない)
        const lt = (a < b);   // true(小さい)
        const le = (a <= b);  // true(以下)
        const gt = (a > b);   // false(大きい)
        const ge = (a >= b);  // false(以上)
    
        std.debug.print("eq={}, ne={}, lt={}, le={}, gt={}, ge={}\n",
            .{eq, ne, lt, le, gt, ge});
    }
    

    ビット演算子

    const std = @import("std");
    
    pub fn example() void {
        const a: u8 = 0b1100;
        const b: u8 = 0b1010;
    
        const and_result = a & b;   // 0b1000 (ビットAND)
        const or_result = a | b;    // 0b1110 (ビットOR)
        const xor_result = a ^ b;   // 0b0110 (ビットXOR)
        const not_result = ~a;      // 0b11110011 (ビットNOT)
    
        const left_shift = a << 2;  // 0b110000 (左シフト)
        const right_shift = a >> 2; // 0b0011 (右シフト)
    
        std.debug.print("and={b:08}, or={b:08}, xor={b:08}\n",
            .{and_result, or_result, xor_result});
    }
    

    論理演算子

    const a = true;
    const b = false;
    
    const and_result = a and b;  // false
    const or_result = a or b;    // true
    const not_result = !a;       // false
    

    デバッグ出力

    std.debug.print

    const std = @import("std");
    
    pub fn example() void {
        // 基本的な出力
        std.debug.print("Hello, World!\n", .{});
    
        // 変数の出力
        const x = 42;
        std.debug.print("x = {}\n", .{x});
    
        // 複数の変数
        const y = 3.14;
        const z = true;
        std.debug.print("x={}, y={d:.2}, z={}\n", .{x, y, z});
    
        // フォーマット指定子
        std.debug.print("{s}\n", .{"文字列"});        // 文字列
        std.debug.print("{}\n", .{42});               // 整数
        std.debug.print("{d:.2}\n", .{3.14159});      // 小数(2桁)
        std.debug.print("{b}\n", .{@as(u8, 15)});     // 2進数
        std.debug.print("{x}\n", .{@as(u8, 255)});    // 16進数
        std.debug.print("{o}\n", .{@as(u8, 8)});      // 8進数
        std.debug.print("{c}\n", .{@as(u8, 'A')});    // 文字
    }
    

    練習例

    例1: 型の範囲を調べる

    const std = @import("std");
    
    pub fn main() void {
        // u8の範囲
        const u8_min: u8 = 0;
        const u8_max: u8 = 255;
        std.debug.print("u8: {} to {}\n", .{u8_min, u8_max});
    
        // i8の範囲
        const i8_min: i8 = -128;
        const i8_max: i8 = 127;
        std.debug.print("i8: {} to {}\n", .{i8_min, i8_max});
    
        // u16の範囲
        const u16_max: u16 = 65535;
        std.debug.print("u16: 0 to {}\n", .{u16_max});
    }
    

    例2: 型変換の実践

    const std = @import("std");
    
    pub fn main() void {
        // 整数から浮動小数点
        const int_val: i32 = 42;
        const float_val: f64 = @floatFromInt(int_val);
        std.debug.print("{} as f64: {d:.1}\n", .{int_val, float_val});
    
        // 浮動小数点から整数
        const pi: f64 = 3.14159;
        const pi_int: i32 = @intFromFloat(pi);
        std.debug.print("{d:.5} as i32: {}\n", .{pi, pi_int});
    
        // 異なる整数型の変換
        const small: i16 = 100;
        const large: i32 = @intCast(small);
        std.debug.print("i16 {} as i32: {}\n", .{small, large});
    }
    

    例3: ビット演算の活用

    const std = @import("std");
    
    pub fn main() void {
        // フラグの管理
        const FLAG_READ: u8 = 0b0001;
        const FLAG_WRITE: u8 = 0b0010;
        const FLAG_EXECUTE: u8 = 0b0100;
    
        var permissions: u8 = FLAG_READ | FLAG_WRITE; // 読み書き
    
        // 実行権限を追加
        permissions |= FLAG_EXECUTE;
    
        // 権限のチェック
        const can_read = (permissions & FLAG_READ) != 0;
        const can_write = (permissions & FLAG_WRITE) != 0;
        const can_execute = (permissions & FLAG_EXECUTE) != 0;
    
        std.debug.print("Permissions: {b:08}\n", .{permissions});
        std.debug.print("Read: {}, Write: {}, Execute: {}\n",
            .{can_read, can_write, can_execute});
    }
    

    まとめ

    この章では、Zigの基本構文と変数について学びました:

  • 基本構文: コメント、セミコロン、ブロック
  • 変数宣言: const(不変)と var(可変)
  • プリミティブ型: 整数、浮動小数点、ブール、文字
  • 型変換: 明示的な型変換が必須
  • 演算子: 算術、比較、ビット、論理演算
  • 次の章では、Zigの型システムについてさらに深く学びます。

    参考資料

  • Zig Language Reference - Types: https://ziglang.org/documentation/master/#Types
  • Zig Language Reference - Operators: https://ziglang.org/documentation/master/#Operators
  • Ziglearn - Basics: https://ziglearn.org/chapter-1/