Zigで私が興奮したことのひとつに、packed structを使ってMMIOを行えるという点があります。
C言語でよくあるビットシフトのダンスは、いつも馬鹿みたいに面倒でエラーを起こしやすいと感じていました。

今ようやく、具体的なユースケースができて、この機能を試す良い口実ができました。そこで自分専用のレジスタマップを生成しているのですが、Zigのドキュメントのこの部分を見逃していたことに気づきました:

pub const GpioRegister = packed struct(u8) {
    GPIO0: bool,
    GPIO1: bool,
    GPIO2: bool,
    GPIO3: bool,
    reserved: u4 = 0,
};

const gpio: *volatile GpioRegister = @ptrFromInt(0x0123);

pub fn writeToGpio(new_states: GpioRegister) void {
    // Example of what not to do:
    // BAD! gpio.GPIO0 = true; BAD!

    // Instead, do this:
    gpio.* = new_states;
}

これには気づきませんでした。面倒そうです。

この有用なスレッドを見つけました : MMIO access restriction using meta programming - #7 by lufe しかし少し…がっかりするかもしれません?

microzigのアプローチは汎用的で堅牢そうですが(何か大きな見落としがない限り)フィールドの自動補完が無効になってしまいます。私は巨大なレジスタマップを扱っており、ストレートな自動補完/レジスタやビットフィールドに対するドキュメントコメントの利用は絶対的なキラーフィーチャーです。

なぜこのような現状なのか、もっと詳しく知っている人はいますか?上記の例で、volatileポインタに対してアトミックな読み書きがデフォルトの動作になっていない理由がわかりません。

floooh July 22, 2026, 8:16am 2

gpio.GPI0 = true;gpio |= GPI0(GPI0がビットマスクの場合)または gpio |= (1<< GPI0)(GPI0がビット位置の場合)と同一だと思います。

メモリマップドレジスタが読み書き可能であれば問題ないはずです?ただし、メモリマップドレジスタが書き込み専用だったり、読み込み時に何らかのアクション(レジスタのクリアなど)をトリガーしたりする場合は、読み-変更-書き込み操作になるため、このパターンは当然壊れます。私は古いホームコンピュータやエミュレータでのMMIO経験しかないので、現代の組み込みシステムについてはわかりません。

似たような用途で、私のエミュレータコードでもpacked structを試してみましたが、最終的には通常の整数ビット操作に戻りました。packed structでは、複数のビットに対する&、|、^や<<、>>などの演算子が、基になる整数へのキャストと戻しなしでは使えないためです。