🦎

MultiArrayListに要素を追加したらpanic: integer overflowになった(Zig)

2023/03/15に公開

タイトルのとおりです。

TL;DR

MultiArrayList.append()すると現在のサイズに追加要素のサイズを足して、capacityを超えないかチェックする。MultiArrayListが初期化されてないと、MultiArrayList.lenが不定値になっており、追加要素のサイズを足したときにinteger overflowが発生する。

MultiArrayListとは?

MultiArrayListは構造体の配列です。通常の配列とは異なり、構造体のメンバごとの配列を複数持つようなデータ構造になっています。
構造体は4byteアライメントが必要で、4byteで切り上げられますが、メンバごとに配列を切り分けて管理することで効率的にメモリを使うことができます。

Mitchell HashimotoさんがZigコンパイラについて解説しているこちらの記事がわかりやすいです。

起きたこと

TL;DRに全部書いてしまいましたが……。
データをMultiArrayListで管理するDmanager構造体をinit()し、dmng.dlist.append()しています。
Dmanager.init()で取得したDmanager構造体は、dlistメンバはundefinedのままです。
デバッグ実行で確認すると、Dmanager.dlist.len12297829382473034410(1 << 128)になっていました。
ここに追加要素のサイズを足したらinteger overflowになりそうですね。

const std = @import("std");

const Data = struct {
    data1: u32,
    data2: u32,
};

const DataList = std.MultiArrayList(Data);
const Dmanager = struct {
    gpa: std.mem.Allocator,
    dlist: DataList = undefined,

    pub fn init(gpa: std.mem.Allocator) Dmanager {
        return Dmanager{
            .gpa = gpa,
        };
    }
};

pub fn main() !void {
    var dmng = Dmanager.init(std.heap.page_allocator);

    try dmng.dlist.append(dmng.gpa,.{
        .data1 = 0,
        .data2 = 2,
    });
}

解決策

当たり前ですが、MultiArrayListの実体を作ればいいです。簡単ですね。

const DataList = std.MultiArrayList(Data);
pub fn main() !void {
    var dmng = Dmanager.init(std.heap.page_allocator);
    dmng.dlist = DataList{};
    try dmng.dlist.append(dmng.gpa,.{
        .data1 = 0,
        .data2 = 2,
    });
}

Discussion