Rollup merge of #138323 - kpreid:offset-of-doc, r=Mark-Simulacrum
Expand and organize `offset_of!` documentation. * 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. * Move `offset_of_enum` documentation to unstable book (with link to it). * Add `offset_of_slice` documentation in unstable book. r? Mark-Simulacrum
This commit is contained in:
commit
5b9225070c
3 changed files with 107 additions and 36 deletions
|
@ -1254,42 +1254,56 @@ impl<T> SizedTypeProperties for T {}
|
||||||
|
|
||||||
/// Expands to the offset in bytes of a field from the beginning of the given type.
|
/// 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.
|
||||||
///
|
///
|
||||||
/// If the nightly-only feature `offset_of_enum` is enabled,
|
/// The offset is returned as a [`usize`].
|
||||||
/// 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:
|
/// # Offsets of, and in, dynamically sized types
|
||||||
///
|
///
|
||||||
/// ```
|
/// The field’s type must be [`Sized`], but it may be located in a [dynamically sized] container.
|
||||||
/// mod nested {
|
/// If the field type is dynamically sized, then you cannot use `offset_of!` (since the field's
|
||||||
/// #[repr(C)]
|
/// alignment, and therefore its offset, may also be dynamic) and must take the offset from an
|
||||||
/// pub struct Struct {
|
/// actual pointer to the container instead.
|
||||||
/// private: u8,
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// // assert_eq!(mem::offset_of!(nested::Struct, private), 0);
|
|
||||||
/// // ^^^ error[E0616]: field `private` of struct `Struct` is private
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Only [`Sized`] fields are supported, but the container may be unsized:
|
|
||||||
/// ```
|
/// ```
|
||||||
/// # use core::mem;
|
/// # use core::mem;
|
||||||
|
/// # use core::fmt::Debug;
|
||||||
/// #[repr(C)]
|
/// #[repr(C)]
|
||||||
/// pub struct Struct {
|
/// pub struct Struct<T: ?Sized> {
|
||||||
/// a: u8,
|
/// a: u8,
|
||||||
/// b: [u8],
|
/// b: T,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK
|
/// #[derive(Debug)]
|
||||||
/// // assert_eq!(mem::offset_of!(Struct, b), 1);
|
/// #[repr(C, align(4))]
|
||||||
/// // ^^^ error[E0277]: doesn't have a size known at compile-time
|
/// 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
|
/// Note that type layout is, in general, [subject to change and
|
||||||
/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If
|
/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If
|
||||||
/// layout stability is required, consider using an [explicit `repr` attribute].
|
/// layout stability is required, consider using an [explicit `repr` attribute].
|
||||||
|
@ -1325,11 +1339,16 @@ impl<T> SizedTypeProperties for T {}
|
||||||
///
|
///
|
||||||
/// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations
|
/// [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
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// #![feature(offset_of_enum)]
|
|
||||||
///
|
|
||||||
/// use std::mem;
|
/// use std::mem;
|
||||||
/// #[repr(C)]
|
/// #[repr(C)]
|
||||||
/// struct FieldStruct {
|
/// struct FieldStruct {
|
||||||
|
@ -1351,18 +1370,11 @@ impl<T> SizedTypeProperties for T {}
|
||||||
/// struct NestedB(u8);
|
/// struct NestedB(u8);
|
||||||
///
|
///
|
||||||
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
|
/// 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")]
|
#[stable(feature = "offset_of", since = "1.77.0")]
|
||||||
#[allow_internal_unstable(builtin_syntax)]
|
#[allow_internal_unstable(builtin_syntax)]
|
||||||
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
|
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue