Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/behavior-considered-undefined.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ A reference/pointer is "dangling" if not all of the bytes it
part of *some* allocation).

r[undefined.dangling.zero-size]
If the size is 0, then the pointer is trivially never "dangling"
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]
Expand Down Expand Up @@ -189,7 +189,7 @@ r[undefined.validity.struct]
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.
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]
Expand Down Expand Up @@ -268,3 +268,4 @@ reading uninitialized memory is permitted are inside `union`s and in "padding"
[unwinding-ffi]: panic.md#unwinding-across-ffi-boundaries
[const-promoted]: destructors.md#constant-promotion
[lifetime-extended]: destructors.md#temporary-lifetime-extension
[zero-sized]: glossary.zst
62 changes: 62 additions & 0 deletions src/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,68 @@ r[glossary.uninhabited]

A type is uninhabited if it has no constructors and therefore can never be instantiated. An uninhabited type is "empty" in the sense that there are no values of the type. The canonical example of an uninhabited type is the [never type] `!`, or an enum with no variants `enum Never { }`. Opposite of [Inhabited](#inhabited).

r[glossary.zst]
### Zero-sized type (ZST)

A type is zero sized (a ZST) if its size is 0. Such types have at most one possible value. Examples include:

- The [unit type] (see [layout.tuple.unit]).
- [Function items] (see [type.fn-item.intro]).
- The constructors of tuple-like structs (see [type.fn-item.intro]).
- The constructors of tuple-like enum variants (see [type.fn-item.intro]).
- `#[repr(C)]` structs with no fields ([unit-like structs]) or where all fields are zero-sized (see [layout.repr.c.struct.size-field-offset]).
- `#[repr(transparent)]` structs with no fields ([unit-like structs]) or where all fields are zero-sized (see [layout.repr.transparent.layout-abi]).
- [Arrays] of zero-sized types (see [layout.array]).
Comment thread
DanielEScherzer marked this conversation as resolved.
- [Arrays] of length zero (see [layout.array]).
- [Unions] of zero-sized types (see [items.union.common-storage]).

```rust
# use core::mem::{size_of, size_of_val};
fn f() {}
struct S(u8);
enum E{ V(u8) }
#[repr(C)]
struct C1 {}
#[repr(C)]
struct C2 {
f1: (),
f2: [(); 10],
f3: [u8; 0],
f4: C1,
}
#[repr(transparent)]
struct T1 {}
#[repr(transparent)]
struct T2 {
f1: (),
f2: [(); 10],
f3: [u8; 0],
}
union U {
f1: (),
f2: [(); 10],
f3: [u8; 0],
}
assert_eq!(0, size_of::<()>());
assert_eq!(0, size_of_val(&f));
// Note that here we are checking the size of the constructors, *not* the
// underlying type, for `S` and `E::V`. The constructors just have the same
// names as the types.
assert_eq!(0, size_of_val(&S));
assert_eq!(0, size_of_val(&E::V));
assert_eq!(0, size_of::<C1>());
assert_eq!(0, size_of::<C2>());
assert_eq!(0, size_of::<T1>());
assert_eq!(0, size_of::<T2>());
assert_eq!(0, size_of::<[(); 10]>());
assert_eq!(0, size_of::<[u8; 0]>());
assert_eq!(0, size_of::<U>());
```

[`extern` blocks]: items.extern
[`extern fn`]: items.fn.extern
[alignment]: type-layout.md#size-and-alignment
[arrays]: type.array
[associated item]: #associated-item
[attributes]: attributes.md
[*entity*]: names.md
Expand All @@ -222,6 +281,7 @@ A type is uninhabited if it has no constructors and therefore can never be insta
[enums]: items/enumerations.md
[fields]: expressions/field-expr.md
[free item]: #free-item
[function items]: type.fn-item
[generic parameters]: items/generics.md
[identifier]: identifiers.md
[identifiers]: identifiers.md
Expand Down Expand Up @@ -252,5 +312,7 @@ A type is uninhabited if it has no constructors and therefore can never be insta
[types]: types.md
[undefined-behavior]: behavior-considered-undefined.md
[unions]: items/unions.md
[unit type]: type.tuple.unit
[unit-like structs]: items.struct.unit
[variable bindings]: patterns.md
[visibility rules]: visibility-and-privacy.md
3 changes: 2 additions & 1 deletion src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ r[items.fn.implicit-return]
If the output type is not explicitly stated, it is the [unit type].

r[items.fn.fn-item-type]
When referred to, a _function_ yields a first-class *value* of the corresponding zero-sized [*function item type*], which when called evaluates to a direct call to the function.
When referred to, a _function_ yields a first-class *value* of the corresponding [zero-sized] [*function item type*], which when called evaluates to a direct call to the function.

For example, this is a simple function:

Expand Down Expand Up @@ -426,3 +426,4 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
[value namespace]: ../names/namespaces.md
[variadic function]: external-blocks.md#variadic-functions
[`extern` block]: external-blocks.md
[zero-sized]: glossary.zst
3 changes: 2 additions & 1 deletion src/special-types-and-traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ mutability aren't placed in memory marked as read only.
r[lang-types.phantom-data]
## `PhantomData<T>`

[`std::marker::PhantomData<T>`] is a zero-sized, minimum alignment, type that
[`std::marker::PhantomData<T>`] is a [zero-sized], minimum alignment, type that
is considered to own a `T` for the purposes of [variance], [drop check], and
[auto traits](#auto-traits).

Expand Down Expand Up @@ -246,6 +246,7 @@ These implicit `Sized` bounds may be relaxed by using the special `?Sized` bound
[test functions]: attributes/testing.md#the-test-attribute
[the standard library]: std
[trait object]: types/trait-object.md
[zero-sized]: glossary.zst
[Tuples]: types/tuple.md
[Type parameters]: types/parameters.md
[variance]: subtyping.md#variance
Expand Down
12 changes: 8 additions & 4 deletions src/type-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ r[layout.properties.align]
The *alignment* of a value specifies what addresses are valid to store the value at. A value of alignment `n` must only be stored at an address that is a multiple of n. For example, a value with an alignment of 2 must be stored at an even address, while a value with an alignment of 1 can be stored at any address. Alignment is measured in bytes, and must be at least 1, and always a power of 2. The alignment of a value can be checked with the [`align_of_val`] function.

r[layout.properties.size]
The *size* of a value is the offset in bytes between successive elements in an array with that item type including alignment padding. The size of a value is always a multiple of its alignment. Note that some types are zero-sized; 0 is considered a multiple of any alignment (for example, on some platforms, the type `[u16; 0]` has size 0 and alignment 2). The size of a value can be checked with the [`size_of_val`] function.
The *size* of a value is the offset in bytes between successive elements in an array with that item type including alignment padding. The size of a value is always a multiple of its alignment. Note that some types are [zero-sized]; 0 is considered a multiple of any alignment (for example, on some platforms, the type `[u16; 0]` has size 0 and alignment 2). The size of a value can be checked with the [`size_of_val`] function.

r[layout.properties.sized]
Types where all values have the same size and alignment, and both are known at compile time, implement the [`Sized`] trait and can be checked with the [`size_of`] and [`align_of`] functions. Types that are not [`Sized`] are known as [dynamically sized types]. Since all values of a `Sized` type share the same size and alignment, we refer to those shared values as the size of the type and the alignment of the type respectively.
Expand Down Expand Up @@ -94,7 +94,7 @@ r[layout.tuple.def]
Tuples are laid out according to the [`Rust` representation][`Rust`].

r[layout.tuple.unit]
The exception to this is the unit tuple (`()`), which is guaranteed as a zero-sized type to have a size of 0 and an alignment of 1.
The exception to this is the unit tuple (`()`), which is guaranteed as a [zero-sized type] to have a size of 0 and an alignment of 1.

r[layout.trait-object]
## Trait object layout
Expand Down Expand Up @@ -178,7 +178,7 @@ The only data layout guarantees made by this representation are those required f
r[layout.repr.rust.layout.struct]
For [structs], it is further guaranteed that the fields do not overlap. That is, the fields can be ordered such that the offset plus the size of any field is less than or equal to the offset of the next field in the ordering. The ordering does not have to be the same as the order in which the fields are specified in the declaration of the type.

Be aware that this guarantee does not imply that the fields have distinct addresses: zero-sized types may have the same address as other fields in the same struct.
Be aware that this guarantee does not imply that the fields have distinct addresses: [zero-sized types] may have the same address as other fields in the same struct.

r[layout.repr.rust.unspecified]
There are no other guarantees of data layout made by this representation.
Expand Down Expand Up @@ -248,7 +248,7 @@ struct.size = current_offset + padding_needed_for(current_offset, struct.alignme
> This pseudocode uses a naive algorithm that ignores overflow issues for the sake of clarity. To perform memory layout computations in actual code, use [`Layout`].

> [!NOTE]
> This algorithm can produce zero-sized structs. In C, an empty struct declaration like `struct Foo { }` is illegal. However, both gcc and clang support options to enable such structs, and assign them size zero. C++, in contrast, gives empty structs a size of 1, unless they are inherited from or they are fields that have the `[[no_unique_address]]` attribute, in which case they do not increase the overall size of the struct.
> This algorithm can produce [zero-sized structs]. In C, an empty struct declaration like `struct Foo { }` is illegal. However, both gcc and clang support options to enable such structs, and assign them size zero. C++, in contrast, gives empty structs a size of 1, unless they are inherited from or they are fields that have the `[[no_unique_address]]` attribute, in which case they do not increase the overall size of the struct.

r[layout.repr.c.union]
#### `#[repr(C)]` Unions
Expand Down Expand Up @@ -561,6 +561,10 @@ Because this representation delegates type layout to another type, it cannot be
[enumerations]: items/enumerations.md
[zero-variant enums]: items/enumerations.md#zero-variant-enums
[undefined behavior]: behavior-considered-undefined.md
[zero-sized]: glossary.zst
[zero-sized type]: glossary.zst
[zero-sized types]: glossary.zst
[zero-sized structs]: glossary.zst
[`PhantomData<T>`]: special-types-and-traits.md#phantomdatat
[`Rust`]: #the-rust-representation
[`C`]: #the-c-representation
Expand Down
3 changes: 2 additions & 1 deletion src/types/function-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ r[type.fn-item]
# Function item types

r[type.fn-item.intro]
When referred to, a function item, or the constructor of a tuple-like struct or enum variant, yields a zero-sized value of its _function item type_.
When referred to, a function item, or the constructor of a tuple-like struct or enum variant, yields a [zero-sized] value of its _function item type_.

r[type.fn-item.unique]
That type explicitly identifies the function - its name, its type arguments, and its early-bound lifetime arguments (but not its late-bound lifetime arguments, which are only assigned when the function is called) - so the value does not need to contain an actual function pointer, and no indirection is needed when the function is called.
Expand Down Expand Up @@ -51,3 +51,4 @@ All function items implement [`Copy`], [`Clone`], [`Send`], and [`Sync`].
[`Sync`]: ../special-types-and-traits.md#sync
[coercion]: ../type-coercions.md
[function pointers]: function-pointer.md
[zero-sized]: glossary.zst
3 changes: 2 additions & 1 deletion src/types/pointer.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ r[type.pointer.validity.pointer-fragment]
Despite pointers and references being similar to `usize`s in the machine code emitted on most platforms, the semantics of transmuting a reference or pointer type to a non-pointer type is currently undecided. Thus, it may not be valid to transmute a pointer or reference type, `P`, to a `[u8; size_of::<P>()]`.

r[type.pointer.validity.raw]
For thin raw pointers (i.e., for `P = *const T` or `P = *mut T` for `T: Sized`), the inverse direction (transmuting from an integer or array of integers to `P`) is always valid. However, the pointer produced via such a transmutation may not be dereferenced (not even if `T` has size zero).
For thin raw pointers (i.e., for `P = *const T` or `P = *mut T` for `T: Sized`), the inverse direction (transmuting from an integer or array of integers to `P`) is always valid. However, the pointer produced via such a transmutation may not be dereferenced (not even if `T` has [size zero]).

[Interior mutability]: ../interior-mutability.md
[`unsafe` operation]: ../unsafety.md
[dynamically sized types]: ../dynamically-sized-types.md
[size zero]: glossary.zst
[temporary value]: ../expressions.md#temporaries
Loading