ifとelse ifにより複数の条件を分岐させることが可能ですが、プログラムの見通しが悪くなります。そこでswitchを使います。Zenのswitchには非常に強力な機能が備わっています。
switchの基本構文は次の通りです。
switch (式) {
    パターン => 処理,
    パターン => 処理,
    ...
    else => 処理,
}
式の値とパターンがマッチした場合、=>演算子の右の処理が実行されます。パターンの部分には、1つの値、,で区切られた複数の値、...で指定された範囲、else、のいずれかを書くことができます。
switchは式の値が取りうる全ての値を網羅しなければなりません。もし式がu8の値だったとすると、switchでは0から255までの数値を網羅しなければなりません。興味のある数値が10より小さいものだけだったりすると、全ての条件を網羅するのは大変です。そこで、elseを使用することができます。
実際のコードを見てみましょう。同じコードをif / if else / elseで書く場合と比較してみて下さい。ずいぶんとスッキリ書けることがわかります。
examples/ch04-basic-syntax/src/switch.zen:4:18
test "switch basic" {
    var x: u8 = 10;
    switch (x) {
        // `x`が0の場合
        0 => std.debug.warn("zero\n", .{}),
        // `x`が1か2か3の場合
        // `,`で条件を複数組み合わせられる
        1, 2, 3 => std.debug.warn("one, two or three\n", .{}),
        // `x`が4以上9以下の場合
        // `...`で条件を範囲で指定できる
        4...9 => std.debug.warn("four to nine\n", .{}),
        // 上記条件のいずれにもマッチしなかった場合
        else => std.debug.warn("ten or more\n", .{}),
    }
}
このコードのstd.debug.warnの出力結果は次の通りです。
ten or more
パターンの値は重複があってはなりません。
    var x: u8 = 12;
    switch (x) {
        12 => {},
        10...15 => {},
        else => {},
    }
このコードは、次のコンパイルエラーになります。
error[E02016]: duplicate value 12
        12 => {},
        ~
note[E00001]: declared here
        10...15 => {},
        ~
switchのパターンは、コンパイル時計算可能であれば、変数や複雑な計算式を置くことができます。
examples/ch04-basic-syntax/src/switch.zen:20:36
test "switch patterns computed in comptime" {
    const ZERO = 0;
    var x: u8 = 2;
    switch (x) {
        // `ZERO`はコンパイル時計算可能
        ZERO => std.debug.warn("zero\n", .{}),
        // `ZERO + 1`はコンパイル時計算可能
        ZERO + 1 => std.debug.warn("one\n", .{}),
        comptime TWO: {
            const a: u32 = 10;
            const b: u32 = 8;
            break :TWO a - b;
        } // ここまでがパターン。パターンは`a-b`の結果`2`になる
        => std.debug.warn("two\n", .{}),
        else => std.debug.warn("three or more\n", .{}),
    }
}
comptimeについては、12.1 comptimeで解説します。
=>の右辺には、ブロック ({}) を書くこともできます。このことにより、複数の文を書くことができます。
examples/ch04-basic-syntax/src/switch.zen:38:49
test "switch case with block" {
    var x: u8 = 4;
    switch (x) {
        // ブロック内に処理を書ける
        0...10 => {
            const is_even = (x % 2) == 0;
            if (is_even) {
                std.debug.warn("0, 2, 4, 6, 8 or 10\n", .{});
            }           
        },
        else => std.debug.warn("ten or more\n", .{}),
    }
}
パターンにマッチした時の値を別変数にキャプチャすることもできます。このとき、マッチした値を参照としてキャプチャすることもできます。
examples/ch04-basic-syntax/src/switch.zen:52:64
test "switch case with capture" {
    var x: u8 = 4;
    switch (x) {
        // `ten_or_less`に値をキャプチャ
        0...10 => |ten_or_less| {
            std.debug.warn("number is: {}\n", .{ten_or_less});        
        },
        // `x`の参照をキャプチャ
        else => |*more_than_ten| { 
            std.debug.warn("number is: {}\n", .{more_than_ten.*});
        },
    }
}
Zenのswitchは式であるため、値を返すことができます。ラベル付きブロックを使用して複雑な計算を行うことも可能です。
examples/ch04-basic-syntax/src/switch.zen:66:79
test "switch expression" {
    var x: u8 = 6;
    const result = switch (x) {
        // `,`の前の値を返す
        0...5 => true,
        // ラベル付きブロックで`break`された値を返す
        else => six_or_more: {
            std.debug.warn("number is six or more\n", .{});
            break :six_or_more false;
        },
    };
    ok(result == false);
}
switchが最も活躍する場面は、列挙型 (およびタグ付き共用体) と組み合わせて利用する時です。重複なく全てのヴァリアントを処理しないとコンパイルエラーになります。
examples/ch04-basic-syntax/src/switch_enum.zen:5:21
const Colors = enum {
    Red,
    Green,
    Blue,
};
test "switch with enum" {
    var color = Colors.Red;
    const result = switch (color) {
        // `.`で列挙型を型推論できる
        .Red => @to(u32, 0),
        .Green => @to(u32, 1),
        .Blue => @to(u32, 2),
    };
    ok(result == 0);
}
次のコードでは、Blueのケースを書き忘れています。
    const result = switch (color) {
        .Red => @to(u32, 0),
        .Green => @to(u32, 1),
        // Blueのケースを書き忘れ
    };
このコードは次のコンパイルエラーになります。
error[E02018]: 'Color.Blue' not handled in switch
    const result = switch (color) {
                   ~
Zenの標準ライブラリでは、プラットフォームごとの違いをswitchとコンパイラ組込みの列挙型とで吸収しています。例えば、OSごとに (コンソールの文字) 色を変えたいとします。その場合、次のコードのように、builtin.os.tagの列挙型でswitchし、os_colorという共通して使える定数に、各OSで使う色を代入しておきます。
examples/ch04-basic-syntax/src/switch_enum.zen:23:27
const os_color = switch(builtin.os.tag) {
    .macosx => .Red,
    .linux => .Green,
    else => .Blue,
};
上記コードにより、下のコードではプラットフォームごとにos_colorが異なった値を持ちます。
examples/ch04-basic-syntax/src/switch_enum.zen:29:35
test "switch with builtin enum" {
    switch (builtin.os.tag) {
        .macosx => ok(os_color == .Red),
        .linux => ok(os_color == .Green),
        else => ok(os_color == .Blue),
    }
}
タグ付き共用体に対してswitchを利用することができます。このことにより、異なるデータ構造をswitchで容易に取り扱うことができます。非常に強力な機能なので、詳しく見ていきましょう。
IPv4 (8ビット×4) アドレスとIPv6 (16ビット×8) アドレスとを表現できるIpAddrタグ付き共用体を考えます。IpAddrはヴァリアントのipv4とipv6とを持ちます。それぞれのヴァリアントをIPv4とIPv6の作法に則って表示するprintAddress関数があります。
examples/ch04-basic-syntax/src/switch_tagged_union.zen:4:25
const IpAddr = union(enum) {
    ipv4: [4]u8,
    ipv6: [8]u16,
    fn printAddress(self: IpAddr) void {
        switch (self) {
            // IPv4の場合、`addr`にヴァリアントの値`[4]u8`をキャプチャ
            .ipv4 => |addr| {
                std.debug.warn("{}:{}:{}:{}\n", .{
                    addr[0], addr[1], addr[2], addr[3],
                });
            },
            // IPv6の場合、`addr`にヴァリアントの値`[8]u16`をキャプチャ
            .ipv6 => |addr| {
                std.debug.warn("{x}:{x}:{x}:{x}:{x}:{x}:{x}:{x}\n", .{
                    addr[0], addr[1], addr[2], addr[3],
                    addr[4], addr[5], addr[6], addr[7],
                });
            },
        }
    }
};
printAddressではswitchを使用してipv4とipv6のヴァリアントをパターンマッチしています。selfで受け取ったIpAddrの値がipv4ヴァリアントであればIPv4の形式で、ipv6のヴァリアントであればIPv6の形式でアドレスが表示されます。
examples/ch04-basic-syntax/src/switch_tagged_union.zen:27:37
test "swith with tagged union" {
    const ipv4 = IpAddr{ .ipv4 = [_]u8{ 192, 168, 0, 1 } };
    const ipv6 = IpAddr{
        .ipv6 = [_]u16{
            0x2001, 0x0db8, 0xbd05, 0x01d2, 0x288a, 0x1fc0, 0x0001, 0x10ee,
        },
    };
    ipv4.printAddress();
    ipv6.printAddress();
}
上記コードを実行すると、std.debug.warnは次の結果を出力します。
192:168:0:1
2001:db8:bd05:1d2:288a:1fc0:1:10ee
      ☰ 人の生きた証は永遠に残るよう ☰
      Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
      Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.