From 58d4395c1ac791dba1da4ca3149a5809693edb7d Mon Sep 17 00:00:00 2001 From: Kevin Reid <kpreid@switchb.org> Date: Mon, 10 Mar 2025 16:01:20 -0700 Subject: [PATCH 1/2] Expand and organize `offset_of!` documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Give example of how to get the offset of an unsized tail field (prompted by discussion <https://github.com/rust-lang/rust/pull/133055#discussion_r1986422206>). * Specify the return type. * Add section headings. * Reduce “Visibility is respected…”, to a single sentence. --- library/core/src/mem/mod.rs | 62 ++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index caab7a6ddb52f..1ebe3f977fdd7 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1249,42 +1249,60 @@ impl<T> SizedTypeProperties for T {} /// Expands to the offset in bytes of a field from the beginning of the given type. /// -/// Structs, enums, unions and tuples are supported. +/// The type may be a `struct`, `enum`, `union`, or tuple. /// -/// Nested field accesses may be used, but not array indexes. +/// The field may be a nested field (`field1.field2`), but not an array index. +/// The field must be visible to the call site. +/// +/// The offset is returned as a [`usize`]. /// /// If the nightly-only feature `offset_of_enum` is enabled, -/// variants may be traversed as if they were fields. +/// `enum` variants may be traversed as if they were fields. /// Variants themselves do not have an offset. /// -/// Visibility is respected - all types and fields must be visible to the call site: -/// -/// ``` -/// mod nested { -/// #[repr(C)] -/// pub struct Struct { -/// private: u8, -/// } -/// } +/// # Offsets of, and in, dynamically sized types /// -/// // assert_eq!(mem::offset_of!(nested::Struct, private), 0); -/// // ^^^ error[E0616]: field `private` of struct `Struct` is private -/// ``` +/// The field’s type must be [`Sized`], but it may be located in a [dynamically sized] container. +/// If the field type is dynamically sized, then you cannot use `offset_of!` (since the field's +/// alignment, and therefore its offset, may also be dynamic) and must take the offset from an +/// actual pointer to the container instead. /// -/// Only [`Sized`] fields are supported, but the container may be unsized: /// ``` /// # use core::mem; +/// # use core::fmt::Debug; /// #[repr(C)] -/// pub struct Struct { +/// pub struct Struct<T: ?Sized> { /// a: u8, -/// b: [u8], +/// b: T, /// } /// -/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK -/// // assert_eq!(mem::offset_of!(Struct, b), 1); -/// // ^^^ error[E0277]: doesn't have a size known at compile-time +/// #[derive(Debug)] +/// #[repr(C, align(4))] +/// struct Align4(u32); +/// +/// assert_eq!(mem::offset_of!(Struct<dyn Debug>, a), 0); // OK — Sized field +/// assert_eq!(mem::offset_of!(Struct<Align4>, b), 4); // OK — not DST +/// +/// // assert_eq!(mem::offset_of!(Struct<dyn Debug>, b), 1); +/// // ^^^ error[E0277]: ... cannot be known at compilation time +/// +/// // To obtain the offset of a !Sized field, examine a concrete value +/// // instead of using offset_of!. +/// let value: Struct<Align4> = Struct { a: 1, b: Align4(2) }; +/// let ref_unsized: &Struct<dyn Debug> = &value; +/// let offset_of_b = unsafe { +/// (&raw const ref_unsized.b).byte_offset_from_unsigned(ref_unsized) +/// }; +/// assert_eq!(offset_of_b, 4); /// ``` /// +/// If you need to obtain the offset of a field of a `!Sized` type, then, since the offset may +/// depend on the particular value being stored (in particular, `dyn Trait` values have a +/// dynamically-determined alignment), you must retrieve the offset from a specific reference +/// or pointer, and so you cannot use `offset_of!` to work without one. +/// +/// # Layout is subject to change +/// /// Note that type layout is, in general, [subject to change and /// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If /// layout stability is required, consider using an [explicit `repr` attribute]. @@ -1358,6 +1376,8 @@ impl<T> SizedTypeProperties for T {} /// /// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); /// ``` +/// +/// [dynamically sized]: https://doc.rust-lang.org/reference/dynamically-sized-types.html #[stable(feature = "offset_of", since = "1.77.0")] #[allow_internal_unstable(builtin_syntax)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { From 8f3254714762438639010efd90a721a133e23cec Mon Sep 17 00:00:00 2001 From: Kevin Reid <kpreid@switchb.org> Date: Mon, 10 Mar 2025 17:27:30 -0700 Subject: [PATCH 2/2] Move `offset_of_enum` documentation to unstable book; add `offset_of_slice`. --- library/core/src/mem/mod.rs | 26 ++++++---------- .../src/language-features/offset-of-enum.md | 29 ++++++++++++++++++ .../src/language-features/offset-of-slice.md | 30 +++++++++++++++++++ 3 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/offset-of-enum.md create mode 100644 src/doc/unstable-book/src/language-features/offset-of-slice.md diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 1ebe3f977fdd7..caf973c53882d 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1256,10 +1256,6 @@ impl<T> SizedTypeProperties for T {} /// /// The offset is returned as a [`usize`]. /// -/// If the nightly-only feature `offset_of_enum` is enabled, -/// `enum` variants may be traversed as if they were fields. -/// Variants themselves do not have an offset. -/// /// # Offsets of, and in, dynamically sized types /// /// The field’s type must be [`Sized`], but it may be located in a [dynamically sized] container. @@ -1338,11 +1334,16 @@ impl<T> SizedTypeProperties for T {} /// /// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations /// +/// # Unstable features +/// +/// The following unstable features expand the functionality of `offset_of!`: +/// +/// * [`offset_of_enum`] — allows `enum` variants to be traversed as if they were fields. +/// * [`offset_of_slice`] — allows getting the offset of a field of type `[T]`. +/// /// # Examples /// /// ``` -/// #![feature(offset_of_enum)] -/// /// use std::mem; /// #[repr(C)] /// struct FieldStruct { @@ -1364,20 +1365,11 @@ impl<T> SizedTypeProperties for T {} /// struct NestedB(u8); /// /// assert_eq!(mem::offset_of!(NestedA, b.0), 0); -/// -/// #[repr(u8)] -/// enum Enum { -/// A(u8, u16), -/// B { one: u8, two: u16 }, -/// } -/// -/// assert_eq!(mem::offset_of!(Enum, A.0), 1); -/// assert_eq!(mem::offset_of!(Enum, B.two), 2); -/// -/// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); /// ``` /// /// [dynamically sized]: https://doc.rust-lang.org/reference/dynamically-sized-types.html +/// [`offset_of_enum`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-enum.html +/// [`offset_of_slice`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-slice.html #[stable(feature = "offset_of", since = "1.77.0")] #[allow_internal_unstable(builtin_syntax)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { diff --git a/src/doc/unstable-book/src/language-features/offset-of-enum.md b/src/doc/unstable-book/src/language-features/offset-of-enum.md new file mode 100644 index 0000000000000..1960d6299eb00 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/offset-of-enum.md @@ -0,0 +1,29 @@ +# `offset_of_slice` + +The tracking issue for this feature is: [#120141] + +[#120141]: https://github.com/rust-lang/rust/issues/120141 + +------------------------ + +When the `offset_of_enum` feature is enabled, the [`offset_of!`] macro may be used to obtain the +offsets of fields of `enum`s; to express this, `enum` variants may be traversed as if they were +fields. Variants themselves do not have an offset, so they cannot appear as the last path component. + +```rust +#![feature(offset_of_enum)] +use std::mem; + +#[repr(u8)] +enum Enum { + A(u8, u16), + B { one: u8, two: u16 }, +} + +assert_eq!(mem::offset_of!(Enum, A.0), 1); +assert_eq!(mem::offset_of!(Enum, B.two), 2); + +assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0); +``` + +[`offset_of!`]: ../../std/mem/macro.offset_of.html diff --git a/src/doc/unstable-book/src/language-features/offset-of-slice.md b/src/doc/unstable-book/src/language-features/offset-of-slice.md new file mode 100644 index 0000000000000..c887fa07f67e1 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/offset-of-slice.md @@ -0,0 +1,30 @@ +# `offset_of_slice` + +The tracking issue for this feature is: [#126151] + +[#126151]: https://github.com/rust-lang/rust/issues/126151 + +------------------------ + +When the `offset_of_slice` feature is enabled, the [`offset_of!`] macro may be used to determine +the offset of fields whose type is `[T]`, that is, a slice of dynamic size. + +In general, fields whose type is dynamically sized do not have statically known offsets because +they do not have statically known alignments. However, `[T]` has the same alignment as `T`, so +it specifically may be allowed. + +```rust +#![feature(offset_of_slice)] + +#[repr(C)] +pub struct Struct { + head: u32, + tail: [u8], +} + +fn main() { + assert_eq!(std::mem::offset_of!(Struct, tail), 4); +} +``` + +[`offset_of!`]: ../../std/mem/macro.offset_of.html