🤏

コンパイル時に型のサイズを確認する

に公開
4

コンパイルタイムに型のサイズをassertしたいときに以下のような方法が使える

SizeCheck struct

const関数がコンパイル時に評価されるのを使ってこのようなコードが書ける。

struct SizeCheck<T, const N: usize>(PhantomData<T>);

impl<T, const N: usize> SizeCheck<T, N> {
    const OK: () = assert!(size_of::<T>() <= N);
}

SizeCheckは型TのPhantomDataとサイズチェックに使う値の二つのジェネリクスパラメータを持つ。
SizeCheckに対して、コンパイル時に評価される定数OKを実装することでコンパイル時サイズチェックを実現できる。

これを使ってこのようなコードを考えてみる。

struct Small {
    _data: [u8; 32]
}

struct Large {
    _data: [u8; 1024]
}

fn main(){
    SizeCheck::<Small, 256>::OK;
    SizeCheck::<Large, 256>::OK;
}

Smallはサイズが256バイト以下なのでコンパイルに成功するが、Largeはサイズが1024バイトなのでコンパイルに失敗する

このrust-playgroundで確認してみてほしい。

[追記]
kanarusさんにご指摘いただきました。このようなジェネリクスを用いなくてもconst _: () = assert!(size_of::<T> /* 条件 */);というような文をコードのどこかに入れるだけで目的が達成されるようです。文中のOKはasociated constというだけでコンパイル時に評価されるconst定数に値をバインドするようにすれば様々なコンパイル時評価が可能なようです。

Discussion

kanaruskanarus

SizeCheckに対して、コンパイル時に評価される関数OKを実装することでコンパイル時サイズチェックを実現できる。

とありますが、

impl<T, const N: usize> SizeCheck<T, N> {
    const OK: () = assert!(size_of::<T>() <= N);
}

associated const (関連定数) です

  • ちなみに現状 associated function は const fn になれない (はず) です
  • また、構造体のサイズを静的に保証するだけなら SizeCheck 構造体を用意する必要もなくて、ソースコードのどこかに const _: () = assert!(size_of::<T> /* 条件 */); を入れておけばよいです (以下の Playground で確認してみてください)

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=61fee44cb25ea647ef0e290e037baf63

  • ちなみに const _: () = ...; みたいなやつを anon const (anonymous const) と呼んだりします
maetinmaetin

trait境界やジェネリクスでどうにかすることばかり考えてしまっていましたね…constだけでも実現できるとは、constへの理解が深まりました。コメントありがとうございます!(それはそれとしてこの書き方はかっこいいので好きです)

kanaruskanarus

(とりあえず、この OK は関数ではないので、そこの表現だけ直しておかれた方がいいかと思います)

maetinmaetin

const関連について内容修正し、追記しました。ご指摘ありがとうございます🙇‍♂️