{"slug": "offset-of-slices", "title": "offset_of! slices", "summary": "A Rust developer created a macro to compute the compile-time offset of slice fields in structs, bypassing the limitation of std::mem::offset_of! on stable Rust. The hack uses a fake instance in a large byte array and pointer arithmetic, with offsets capped at 64KB to avoid undefined behavior.", "body_md": "[ std::mem::offset_of!](https://doc.rust-lang.org/stable/std/mem/macro.offset_of.html) is a helpful little macro that lets you compute the offset of a particular field of a\n\n`struct`\n\nat compile time. I haven’t had much use for it myself, but [a friend](https://terts.dev/)has been using it for\n\n[a cool JIT’ed scripting language for Rust](https://codeberg.org/NLnetLabs/roto), so it’s come up occasionally.\n\nIt has a few quirks, though: you can’t use it for unsized fields (e.g. `b`\n\nin `Foo { a: u8, b: [u8] }`\n\n). This isn’t too common, but if you’re doing weird enough things that you need to compute byte offsets of fields, you are probably familiar with (and happy to (ab)use) dynamically sized types. Like all frustrating things, there’s a reason beneath the surface: in Rust, types that implement `Sized`\n\nhave a fixed size *and alignment*, while others do not. With a type like `Bar { a: u8, b: dyn Debug }`\n\n, `b`\n\ncould have an arbitrary type, thus an arbitrary alignment, thus an arbitrary offset.\n\nSlices are a bit of an exception here: they do not implement `Sized`\n\n, and their size is not fixed at compile time, but their alignment **is**. The alignment of `[T]`\n\nis just the alignment of `T`\n\n. So the offset of a slice field in a `struct`\n\ncan, in theory, be computed. `std::mem::offset_of!`\n\nsupports this under the unstable `offset_of_slice`\n\nfeature … but what if you wanted this behavior on stable Rust?\n\nBehold (also [on the Rust playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=05491a0a69fb38c99d1dde6fdf0051ee)):\n\n``` js\nmacro_rules! fun_offset_of {  ($t:ty, $field:ident) => {    const {      // An empty space we can play within.      const EMPTY: &[u8] = &[0u8; 65536];      let empty: *const [u8] = &raw const *EMPTY;      // This cast only compiles if `$t` is `Sized` or has      // slice metadata.      let container = empty as *const $t;      // Now, extract the field. This is technically      // `unsafe`, but since we're in a `const` block,      // undefined behavior will be a compilation error.      let field = unsafe { &raw const (*container).$field };      // Compute the offset between `container` and      // `field`, in units of bytes. We satisfy the basic      // requirements of `offset_from`: both are derived      // from the same allocation, `*EMPTY`.      let container = container.cast::<u8>();      let field = field.cast::<u8>();      unsafe { field.offset_from(container) }    }  };}\n```\n\nThe idea is pretty simple: we can construct a fake instance of the containing type, get a pointer to the field within it, and return the offset of that pointer. All of these operations can be performed at compile-time! Due to [the interesting semantics of pointer casts](https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast), the macro will only compile with containing types that are `Sized`\n\nor end with slice fields (the exact case we want to support).\n\nUnfortunately, Rust requires (for `&raw const (*container).$field`\n\n) that the fake instance point to valid memory and the offset be in-bounds. We can’t just use a null pointer. It doesn’t care about the contents of that memory, so we do the simple thing and build a big byte array. The offset of the field must lie within this memory, so offsets larger than 64k won’t work, but I think that’s a manageable limitation. Undefined behavior at compile time will (always) be turned into a compilation error!\n\nBefore `std::mem::offset_of!`\n\nwas stabilized, there were a few crates that provided similar functionality, e.g. [ offset](https://docs.rs/offset),\n\n[,](https://docs.rs/memoffset)\n\n`memoffset`\n\n[, etc. As far as I can tell, they mostly just use](https://docs.rs/repr_offset)\n\n`repr_offset`\n\n`std::mem::offset_of!`\n\nnow, and never supported getting the offset of a slice field. Maybe somebody else has invented a similar hack before and hidden it away in an unrelated crate, but I couldn’t find anything obvious.I didn’t need this functionality but it was fun to come up with. If this story needs a moral, it’s that you should have fun and commit Rust crimes :3", "url": "https://wpnews.pro/news/offset-of-slices", "canonical_source": "https://bal-e.org/blog/2026/offset-of-slices/", "published_at": "2026-06-18 14:56:17+00:00", "updated_at": "2026-06-18 15:32:11.610282+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Rust", "std::mem::offset_of!", "NLnet Labs", "roto"], "alternates": {"html": "https://wpnews.pro/news/offset-of-slices", "markdown": "https://wpnews.pro/news/offset-of-slices.md", "text": "https://wpnews.pro/news/offset-of-slices.txt", "jsonld": "https://wpnews.pro/news/offset-of-slices.jsonld"}}