The offset method on pointers is unsafe. The wrapping_offset method is not. Apparently the difference is in what sort of assumptions LLVM is allowed to make and the former in theory can have better codegen. naturally I want to write the fastest possible code So I'm trying to wrap my head around when I can get away with using
offset
.
I'm normally writing code for modern 64-bit machines which have huge address spaces, so I'm not worried about these requirements:
- The computed offset, in bytes , cannot overflow an
isize
.- The offset being in bounds cannot rely on "wrapping around" the address space. That is, the infinite-precision sum, in bytes must fit in a usize.
But I find this one much harder to reason about:
- 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.
Say using unsafe I mmap
a large empty memory region, that I want to use as backing memory for many contiguous instances of some T
. To construct these instances I'm going to repeatedly take the *mut u8
that mmap
gave me, offset by i*size_of<T>::()
, cast it to *mut MaybeUninit<T>
, and do a ptr::write
to initialize. Is that legal? Or do I have to use wrapping_offset
? Did mmap
create one logical "allocation"? What if I mmap
a large region with PROT_NONE
, then later change some of the region to be read/write with mprotect
?