`offset`ではなく,整数型にキャストして演算する
OSを書く場合などでは,しばしばあるアドレスを基準とした相対アドレスが必要となる場合があります.例えばVideo RAM(VRAM)へのアクセスでは,VRAMの開始アドレスを基準として,アクセスしたい画面上の座標と,解像度を用いて最終的なVRAM上の番地を確定します.
let ptr = vram.base + y * vram.resolution.x + x;
この時,base
が生ポインタである場合,一見するとoffset
メソッドが使用可能であると思うかもしれません.
let ptr = vram.base.offset(y * vram.resolution.x + x);
しかし,Rustの公式ドキュメントによれば,これは未定義動作を引き起こすとされています.以下は,公式ドキュメントのoffset
の項の,Safetyにおける破ってはいけない規則の1つ目のものです.
Both the starting and resulting pointer must be either in bounds or one byte past the end of the same allocated object. Note that in Rust, every (stack-allocated) variable is considered a separate allocated object.
つまり,ベースポインタとオフセット後のポインタが,同一のオブジェクトのメモリ領域内か,それより1バイト後ろを指していなければなりません.
また,wrapping_offset
のSafetyの説明には,以下のように説明されています.
If you need to cross object boundaries, cast the pointer to an integer and do the arithmetic there.
つまり,オブジェクトの境界を超える場合,整数型にキャストしてから計算を行なう必要があります.
let ptr = (vram.base as usize + y * vram.resolution.x + x) as *mut _;