From e9ebd569db4991ef956278740732707c083eb634 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sun, 7 Jun 2026 23:01:46 +0000 Subject: [PATCH 1/5] Define pointer metadata The pointer-to-pointer cast rules and the wide-pointer validity rule both speak of the *metadata* of a pointer, but we hadn't explicitly defined the term (even though we had defined the contents of that metadata). Let's do that and link to it. --- src/behavior-considered-undefined.md | 9 +++++---- src/dynamically-sized-types.md | 6 +++--- src/expressions/operator-expr.md | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index c09666cc62..0e3228916d 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -100,7 +100,7 @@ r[undefined.dangling.zero-size] If the [size is 0][zero-sized], then the pointer is trivially never "dangling" (even if it is a null pointer). r[undefined.dangling.dynamic-size] -Note that dynamically sized types (such as slices and strings) point to their entire range, so it is important that the length metadata is never too large. +Note that dynamically sized types (such as slices and strings) point to their entire range, so it is important that the length [metadata] is never too large. r[undefined.dangling.alloc-limit] In particular, the dynamic size of a Rust value (as determined by `size_of_val`) must never exceed `isize::MAX`, since it is impossible for a single allocation to be larger than `isize::MAX`. @@ -142,10 +142,10 @@ r[undefined.validity.union] * For a `union`, the exact validity requirements are not decided yet. Obviously, all values that can be created entirely in safe code are valid. If the union has a [zero-sized] field, then every possible value is valid. Further details are [still being debated](https://github.com/rust-lang/unsafe-code-guidelines/issues/438). r[undefined.validity.reference-box] -* A reference or [`Box`] must be aligned and non-null, it cannot be [dangling], and it must point to a valid value (in case of dynamically sized types, using the actual dynamic type of the pointee as determined by the metadata). Note that the last point (about pointing to a valid value) remains a subject of some debate. +* A reference or [`Box`] must be aligned and non-null, it cannot be [dangling], and it must point to a valid value (in case of dynamically sized types, using the actual dynamic type of the pointee as determined by the [metadata]). Note that the last point (about pointing to a valid value) remains a subject of some debate. r[undefined.validity.wide] -* The metadata of a wide reference, [`Box`], or raw pointer must match the type of the unsized tail: +* The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the unsized tail: * `dyn Trait` metadata must be a pointer to a compiler-generated vtable for `Trait`. (For raw pointers, this requirement remains a subject of some debate.) * Slice (`[T]`) metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], slice metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. @@ -156,7 +156,7 @@ r[undefined.validity.valid-range] > `rustc` achieves this with the unstable `rustc_layout_scalar_valid_range_*` attributes. r[undefined.validity.const-provenance] -* **In [const contexts]**: In addition to what is described above, further provenance-related requirements apply during const evaluation. Any value that holds pure integer data (the `i*`/`u*`/`f*` types as well as `bool` and `char`, enum discriminants, and slice metadata) must not carry any provenance. Any value that holds pointer data (references, raw pointers, function pointers, and `dyn Trait` metadata) must either carry no provenance, or all bytes must be fragments of the same original pointer value in the correct order. +* **In [const contexts]**: In addition to what is described above, further provenance-related requirements apply during const evaluation. Any value that holds pure integer data (the `i*`/`u*`/`f*` types as well as `bool` and `char`, enum discriminants, and slice [metadata]) must not carry any provenance. Any value that holds pointer data (references, raw pointers, function pointers, and `dyn Trait` metadata) must either carry no provenance, or all bytes must be fragments of the same original pointer value in the correct order. This implies that transmuting or otherwise reinterpreting a pointer (reference, raw pointer, or function pointer) into a non-pointer type (such as integers) is undefined behavior if the pointer had provenance. @@ -194,6 +194,7 @@ r[undefined.validity.undef] [`target_feature`]: attributes/codegen.md#the-target_feature-attribute [`UnsafeCell`]: std::cell::UnsafeCell [Rustonomicon]: ../nomicon/index.html +[metadata]: dynamic-sized.pointer-types [`NonNull`]: core::ptr::NonNull [`NonZero`]: core::num::NonZero [place expression context]: expressions.md#place-expressions-and-value-expressions diff --git a/src/dynamically-sized-types.md b/src/dynamically-sized-types.md index cd90adb75d..5c9e69a713 100644 --- a/src/dynamically-sized-types.md +++ b/src/dynamically-sized-types.md @@ -8,9 +8,9 @@ r[dynamic-sized.restriction] Such types can only be used in certain cases: r[dynamic-sized.pointer-types] -* [Pointer types] to DSTs are sized but have twice the size of pointers to sized types - * Pointers to slices and `str` also store the number of elements. - * Pointers to trait objects also store a pointer to a vtable. +* [Pointer types] to DSTs are sized but have twice the size of pointers to sized types, since they also store *metadata*: + * Pointers to slices store the number of elements; pointers to `str` store the length in bytes. + * Pointers to trait objects store a pointer to a vtable. r[dynamic-sized.question-sized] * DSTs can be provided as type arguments to generic type parameters having the special `?Sized` bound. They can also be used for associated type definitions when the corresponding associated type declaration has a `?Sized` bound. By default, any type parameter or associated type has a `Sized` bound, unless it is relaxed using `?Sized`. diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 6c5e4ffbf1..c692122bf4 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -732,7 +732,7 @@ r[expr.as.pointer.sized] > ``` r[expr.as.pointer.discard-metadata] -- If `T` is unsized and `U` is sized, the cast discards all metadata that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer. +- If `T` is unsized and `U` is sized, the cast discards all [metadata] that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer. > [!EXAMPLE] > ```rust @@ -1244,6 +1244,7 @@ As with normal assignment expressions, compound assignment expressions always pr [logical not]: ../types/boolean.md#logical-not [logical or]: ../types/boolean.md#logical-or [logical xor]: ../types/boolean.md#logical-xor +[metadata]: dynamic-sized.pointer-types [moved from]: expr.move.movable-place [mutable]: ../expressions.md#mutability [place expression]: ../expressions.md#place-expressions-and-value-expressions From 39fa8df6318c878f917537dbd48a4ec8be7ac435 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sun, 7 Jun 2026 23:12:59 +0000 Subject: [PATCH 2/5] Define an unsized tail We refer to the unsized tail of a type, but we hadn't defined it. Let's do that and link to the definition. --- src/behavior-considered-undefined.md | 3 ++- src/dynamically-sized-types.md | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index 0e3228916d..ef6e889e3a 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -145,7 +145,7 @@ r[undefined.validity.reference-box] * A reference or [`Box`] must be aligned and non-null, it cannot be [dangling], and it must point to a valid value (in case of dynamically sized types, using the actual dynamic type of the pointee as determined by the [metadata]). Note that the last point (about pointing to a valid value) remains a subject of some debate. r[undefined.validity.wide] -* The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the unsized tail: +* The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the [unsized tail]: * `dyn Trait` metadata must be a pointer to a compiler-generated vtable for `Trait`. (For raw pointers, this requirement remains a subject of some debate.) * Slice (`[T]`) metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], slice metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. @@ -204,6 +204,7 @@ r[undefined.validity.undef] [project-field]: expressions/field-expr.md [project-tuple]: expressions/tuple-expr.md#tuple-indexing-expressions [project-slice]: expressions/array-expr.md#array-and-slice-indexing-expressions +[unsized tail]: dynamic-sized.tail [unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries [const-promoted]: destructors.md#constant-promotion [lifetime-extended]: destructors.md#temporary-lifetime-extension diff --git a/src/dynamically-sized-types.md b/src/dynamically-sized-types.md index 5c9e69a713..b14a807e83 100644 --- a/src/dynamically-sized-types.md +++ b/src/dynamically-sized-types.md @@ -25,10 +25,17 @@ r[dynamic-sized.struct-field] > [!NOTE] > [Variables], function parameters, [const] items, and [static] items must be `Sized`. +r[dynamic-sized.tail] +The *unsized tail* of a type is the dynamically sized component that the [metadata] of a pointer to the type describes. A [slice] (`[T]`) and a [`str`] are each their own unsized tail, described by a length; a [trait object] (`dyn Trait`) is its own unsized tail, described by a pointer to a vtable. When a struct (per [dynamic-sized.struct-field]) or a tuple has an unsized last field, its unsized tail is the unsized tail of that field. A sized type has no unsized tail. + +[metadata]: dynamic-sized.pointer-types [sized]: special-types-and-traits.md#sized [Slices]: types/slice.md +[slice]: types/slice.md [str]: types/str.md +[`str`]: types/str.md [trait objects]: types/trait-object.md +[trait object]: types/trait-object.md [Pointer types]: types/pointer.md [Variables]: variables.md [const]: items/constant-items.md From 082d04d81e878dc18774707ae68d55bc93fdaf09 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Tue, 9 Jun 2026 21:09:42 +0000 Subject: [PATCH 3/5] Cover `str` in the wide-pointer metadata rule The validity rule for the metadata of a wide reference, `Box`, or raw pointer mentions `dyn Trait` and slice but had omitted `str`. Let's fix that. --- src/behavior-considered-undefined.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index ef6e889e3a..a4a285cc9a 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -147,7 +147,7 @@ r[undefined.validity.reference-box] r[undefined.validity.wide] * The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the [unsized tail]: * `dyn Trait` metadata must be a pointer to a compiler-generated vtable for `Trait`. (For raw pointers, this requirement remains a subject of some debate.) - * Slice (`[T]`) metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], slice metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. + * Slice (`[T]`) and `str` metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], this metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. r[undefined.validity.valid-range] * If a type has a custom range of valid values, then a valid value must be in that range. In the standard library, this affects [`NonNull`] and [`NonZero`]. From 4a5f81c16ff071420a9acca11f406d773d246fb2 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 10 Jun 2026 19:46:58 +0000 Subject: [PATCH 4/5] Generalize the wide-pointer total-size bound We document that, for references and `Box`, pointed-to values with slice or `str` metadata must be no larger than `isize::MAX`. We hadn't required this for pointed-to values with `dyn` metadata. It's tempting to think this isn't necessary since we separately require that the metadata point to a vtable generated by the compiler, which ensures the encoded size of the erased type is OK. But the bound is on the total size of the pointed-to value, including any sized prefix of a type with an unsized tail. Since the prefix combined with the size in the vtable can push us past the limit, we need the separate restriction. Let's apply the rule to both cases and add an admonition to remind ourselves of why this is needed. --- src/behavior-considered-undefined.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/behavior-considered-undefined.md b/src/behavior-considered-undefined.md index a4a285cc9a..28ec9f5ade 100644 --- a/src/behavior-considered-undefined.md +++ b/src/behavior-considered-undefined.md @@ -147,7 +147,12 @@ r[undefined.validity.reference-box] r[undefined.validity.wide] * The [metadata] of a wide reference, [`Box`], or raw pointer must match the type of the [unsized tail]: * `dyn Trait` metadata must be a pointer to a compiler-generated vtable for `Trait`. (For raw pointers, this requirement remains a subject of some debate.) - * Slice (`[T]`) and `str` metadata must be a valid `usize`. Furthermore, for wide references and [`Box`], this metadata is invalid if it makes the total size of the pointed-to value bigger than `isize::MAX`. + * Slice (`[T]`) and `str` metadata must be a valid `usize`. + + In addition, for a wide reference or [`Box`], the metadata is invalid if it makes the total size of the pointed-to value (as determined by `size_of_val`) bigger than `isize::MAX`. + + > [!NOTE] + > This bound is on the size of the entire pointed-to value, not just its unsized tail, and it constrains `dyn Trait` metadata just as it does a slice or `str` length. A valid vtable describes an erased type no larger than `isize::MAX`, but a sized prefix can still carry the total past the limit. r[undefined.validity.valid-range] * If a type has a custom range of valid values, then a valid value must be in that range. In the standard library, this affects [`NonNull`] and [`NonZero`]. From ab30b8a703cca5513eed4e5d95a174b65485949d Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Thu, 11 Jun 2026 07:10:46 +0000 Subject: [PATCH 5/5] Document metadata of ptrs to indirectly unsized types We say that pointers to DSTs store metadata and what that metadata is for pointers to slices, `str`, and trait objects. But a struct or tuple with an unsized tail is itself a DST, and we hadn't said what the metadata is for pointers to these unsized types. Now that we've defined *metadata* and *unsized tail*, let's complete this enumeration. --- src/dynamically-sized-types.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dynamically-sized-types.md b/src/dynamically-sized-types.md index b14a807e83..52c7ac3f43 100644 --- a/src/dynamically-sized-types.md +++ b/src/dynamically-sized-types.md @@ -11,6 +11,7 @@ r[dynamic-sized.pointer-types] * [Pointer types] to DSTs are sized but have twice the size of pointers to sized types, since they also store *metadata*: * Pointers to slices store the number of elements; pointers to `str` store the length in bytes. * Pointers to trait objects store a pointer to a vtable. + * Pointers to a struct or tuple with an [unsized tail] store the same metadata as a pointer to that tail. r[dynamic-sized.question-sized] * DSTs can be provided as type arguments to generic type parameters having the special `?Sized` bound. They can also be used for associated type definitions when the corresponding associated type declaration has a `?Sized` bound. By default, any type parameter or associated type has a `Sized` bound, unless it is relaxed using `?Sized`. @@ -30,6 +31,7 @@ The *unsized tail* of a type is the dynamically sized component that the [metada [metadata]: dynamic-sized.pointer-types [sized]: special-types-and-traits.md#sized +[unsized tail]: dynamic-sized.tail [Slices]: types/slice.md [slice]: types/slice.md [str]: types/str.md