🙆

Zigの配列とスライスの型まとめ

2023/10/30に公開

想定読者

  • zigの配列とスライスの型がいまいちピンと来ていない方

配列とスライスの関係

スライス = 配列へのポインタ + 配列のサイズ(usize)

配列の裏にはSliceがあるイメージです。

配列とスライスの型

データの型i32で要素数が5の場合は配列とスライスの型は次のようになります。

test "array_and_slice" {
    var array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(array));

    var K: usize = 0;
    var slice = array[0..K];
    try std.testing.expectEqual([]i32, @TypeOf(slice));
}

配列の型は要素数を含んでいることが分かりますね。

varとconst

zigの変数はvarconstの2種類の宣言方法があります。

ざっくり言うと、varmutableな変数でconstimmutableな変数です。(詳細は下記を参考にしてください。)

https://ziglearn.org/chapter-1/#assignment

配列はvarとconstの2種類の宣言方法があります。

test "mutable_and_imutable_array" {
    var mutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(mutable_array));

    const immutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(immutable_array));
}

4種類のスライス

裏にある配列にvarとconstの2種類あり、スライス自身にもvarとconstの2種類あるため、スライスには次の4種類に分類することができます。

1. const宣言で定義された配列 + const宣言で定義されたスライス
2. const宣言で定義された配列 + var宣言で定義されたスライス
3. var宣言で定義された配列   + const宣言で定義されたスライス
4. var宣言で定義された配列   + var宣言で定義されたスライス

それぞれがどのような型になっているか見てみましょう。

配列がconstの場合のスライスの型

1. const宣言で定義された配列 + const宣言で定義されたスライス
2. const宣言で定義された配列 + var宣言で定義されたスライス

のパターンについては、どちらもconstなスライス型になります。

test "slice_from_immutable_array" {
    const immutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(immutable_array));

    var K: usize = 0;

    var mutable_slice = immutable_array[0..K];
    try std.testing.expectEqual([]const i32, @TypeOf(mutable_slice));

    const immutable_slice = immutable_array[0..K];
    try std.testing.expectEqual([]const i32, @TypeOf(immutable_slice));
}

配列がvarの場合のスライスの型

test "slice_from_mutable_array" {
    var mutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(mutable_array));

    var K: usize = 0;

    var mutable_slice = mutable_array[0..K];
    try std.testing.expectEqual([]i32, @TypeOf(mutable_slice));

    const immutable_slice = mutable_array[0..K];
    try std.testing.expectEqual([]i32, @TypeOf(immutable_slice));
}

注意: コンパイル時に値の確定するスライスは配列のポインタになる

コンパイル時に値が確定していると、スライスはただの配列へのポインタとして扱われます。

test "slice_from_array" {
    var array = [_]i32{ 1, 2, 3, 4, 5 };
    
    var slice = array[0..];
    try std.testing.expectEqual(*[5]i32, @TypeOf(slice));
}

まとめ

const std = @import("std");
const stdout = std.io.getStdOut().writer();

test "test_array_or_slice_from_immutable_array" {
    const immutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(immutable_array));

    var K: usize = 0;

    var mutable_slice = immutable_array[0..K];
    try std.testing.expectEqual([]const i32, @TypeOf(mutable_slice));

    const immutable_slice = immutable_array[0..K];
    try std.testing.expectEqual([]const i32, @TypeOf(immutable_slice));

    // if the length of the slice is known at compile time,
    // zig handle the type of the slice as a pointer to an array
    var mutable_ptr_to_array = immutable_array[0..];
    try std.testing.expectEqual(*const [5]i32, @TypeOf(mutable_ptr_to_array));

    // if the length of the slice is unknown at compile time,
    // zig handle the type of the slice as a pointer to an array
    const immutable_ptr_to_array = immutable_array[0..];
    try std.testing.expectEqual(*const [5]i32, @TypeOf(immutable_ptr_to_array));
}

test "test_array_and_slice_from_mutable_array" {
    var mutable_array = [_]i32{ 1, 2, 3, 4, 5 };
    try std.testing.expectEqual([5]i32, @TypeOf(mutable_array));

    var K: usize = 0;

    var mutable_slice = mutable_array[0..K];
    try std.testing.expectEqual([]i32, @TypeOf(mutable_slice));

    const immutable_slice = mutable_array[0..K];
    try std.testing.expectEqual([]i32, @TypeOf(immutable_slice));

    // if the length of the slice is known at compile time,
    // zig handle the type of the slice as a pointer to an array
    var mutable_ptr_to_array = mutable_array[0..];
    try std.testing.expectEqual(*[5]i32, @TypeOf(mutable_ptr_to_array));

    // if the length of the slice is unknown at compile time,
    // zig handle the type of the slice as a pointer to an array
    const immutable_ptr_to_array = mutable_array[0..];
    try std.testing.expectEqual(*[5]i32, @TypeOf(immutable_ptr_to_array));
}

Discussion