diff --git a/src/types.md b/src/types.md index 0f8263835..a93f93d1f 100644 --- a/src/types.md +++ b/src/types.md @@ -1,14 +1,21 @@ {{#include types-redirect.html}} # Types +r[type] + +r[type.intro] Every variable, item, and value in a Rust program has a type. The _type_ of a *value* defines the interpretation of the memory holding it and the operations that may be performed on the value. +r[type.builtin] Built-in types are tightly integrated into the language, in nontrivial ways -that are not possible to emulate in user-defined types. User-defined types have -limited capabilities. +that are not possible to emulate in user-defined types. + +r[type.user-defined] +User-defined types have limited capabilities. +r[type.kinds] The list of types is: * Primitive types: @@ -37,6 +44,9 @@ The list of types is: ## Type expressions +r[type.name] + +r[type.name.syntax] > **Syntax**\ > _Type_ :\ >       _TypeNoBounds_\ @@ -59,27 +69,47 @@ The list of types is: >    | [_BareFunctionType_]\ >    | [_MacroInvocation_] +r[type.name.intro] A _type expression_ as defined in the _Type_ grammar rule above is the syntax for referring to a type. It may refer to: +r[type.name.sequence] * Sequence types ([tuple], [array], [slice]). + +r[type.name.path] * [Type paths] which can reference: * Primitive types ([boolean], [numeric], [textual]). * Paths to an [item] ([struct], [enum], [union], [type alias], [trait]). * [`Self` path] where `Self` is the implementing type. * Generic [type parameters]. + +r[type.name.pointer] * Pointer types ([reference], [raw pointer], [function pointer]). + +r[type.name.inference] * The [inferred type] which asks the compiler to determine the type. + +r[type.name.grouped] * [Parentheses] which are used for disambiguation. + +r[type.name.trait] * Trait types: [Trait objects] and [impl trait]. + +r[type.name.never] * The [never] type. + +r[type.name.macro-expansion] * [Macros] which expand to a type expression. ### Parenthesized types +r[type.name.parenthesized] + +r[type.name.parenthesized.syntax] > _ParenthesizedType_ :\ >    `(` [_Type_] `)` +r[type.name.parenthesized.intro] In some situations the combination of types may be ambiguous. Use parentheses around a type to avoid ambiguity. For example, the `+` operator for [type boundaries] within a [reference type] is unclear where the @@ -94,10 +124,16 @@ type T<'a> = &'a (dyn Any + Send); ## Recursive types +r[type.recursive] + +r[type.recursive.intro] Nominal types — [structs], [enumerations], and [unions] — may be recursive. That is, each `enum` variant or `struct` or `union` field may refer, directly or indirectly, to the enclosing `enum` or `struct` type -itself. Such recursion has restrictions: +itself. + +r[type.recursive.constraint] +Such recursion has restrictions: * Recursive types must include a nominal type in the recursion (not mere [type aliases], or other structural types such as [arrays] or [tuples]). So `type diff --git a/src/types/array.md b/src/types/array.md index 167954bfe..ef54af1f3 100644 --- a/src/types/array.md +++ b/src/types/array.md @@ -1,12 +1,18 @@ # Array types +r[type.array] + +r[type.array.syntax] > **Syntax**\ > _ArrayType_ :\ >    `[` [_Type_] `;` [_Expression_] `]` +r[type.array.intro] An array is a fixed-size sequence of `N` elements of type `T`. The array type -is written as `[T; N]`. The size is a [constant expression] that evaluates to a -[`usize`]. +is written as `[T; N]`. + +r[type.array.constraint] +The size is a [constant expression] that evaluates to a [`usize`]. Examples: @@ -18,6 +24,7 @@ let array: [i32; 3] = [1, 2, 3]; let boxed_array: Box<[i32]> = Box::new([1, 2, 3]); ``` +r[type.array.index] All elements of arrays are always initialized, and access to an array is always bounds-checked in safe methods and operators. diff --git a/src/types/boolean.md b/src/types/boolean.md index a0c07101f..10c6e5de1 100644 --- a/src/types/boolean.md +++ b/src/types/boolean.md @@ -1,31 +1,44 @@ # Boolean type +r[type.bool] + ```rust let b: bool = true; ``` +r[type.bool.intro] The *boolean type* or *bool* is a primitive data type that can take on one of two values, called *true* and *false*. +r[type.bool.literal] Values of this type may be created using a [literal expression] using the keywords `true` and `false` corresponding to the value of the same name. +r[type.bool.namespace] This type is a part of the [language prelude] with the [name] `bool`. -An object with the boolean type has a [size and alignment] of 1 each. The -value false has the bit pattern `0x00` and the value true has the bit pattern +r[type.bool.layout] +An object with the boolean type has a [size and alignment] of 1 each. + +r[type.bool.repr] +The value false has the bit pattern `0x00` and the value true has the bit pattern `0x01`. It is [undefined behavior] for an object with the boolean type to have any other bit pattern. +r[type.bool.usage] The boolean type is the type of many operands in various [expressions]: +r[type.bool.usage-condition] * The condition operand in [if expressions] and [while expressions] + +r[type.bool.usage-lazy-operator] * The operands in [lazy boolean operator expressions][lazy] > **Note**: The boolean type acts similarly to but is not an [enumerated type]. In practice, this mostly means that constructors are not associated to the type (e.g. `bool::true`). +r[type.bool.traits] Like all primitives, the boolean type [implements][p-impl] the [traits][p-traits] [`Clone`][p-clone], [`Copy`][p-copy], [`Sized`][p-sized], [`Send`][p-send], and [`Sync`][p-sync]. @@ -34,11 +47,15 @@ Like all primitives, the boolean type [implements][p-impl] the ## Operations on boolean values +r[type.bool.expr] + When using certain operator expressions with a boolean type for its operands, they evaluate using the rules of [boolean logic]. ### Logical not +r[type.bool.expr.not] + | `b` | [`!b`][op-not] | |- | - | | `true` | `false` | @@ -46,6 +63,8 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. ### Logical or +r[type.bool.expr.or] + | `a` | `b` | [a | b][op-or] | |- | - | - | | `true` | `true` | `true` | @@ -55,6 +74,8 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. ### Logical and +r[type.bool.expr.and] + | `a` | `b` | [`a & b`][op-and] | |- | - | - | | `true` | `true` | `true` | @@ -64,6 +85,8 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. ### Logical xor +r[type.bool.expr.xor] + | `a` | `b` | [`a ^ b`][op-xor] | |- | - | - | | `true` | `true` | `false` | @@ -73,6 +96,9 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. ### Comparisons +r[type.bool.expr.cmp] + +r[type.bool.expr.cmp.eq] | `a` | `b` | [`a == b`][op-compare] | |- | - | - | | `true` | `true` | `true` | @@ -80,6 +106,7 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. | `false` | `true` | `false` | | `false` | `false` | `true` | +r[type.bool.expr.cmp.greater] | `a` | `b` | [`a > b`][op-compare] | |- | - | - | | `true` | `true` | `false` | @@ -87,13 +114,22 @@ boolean type for its operands, they evaluate using the rules of [boolean logic]. | `false` | `true` | `false` | | `false` | `false` | `false` | +r[type.bool.expr.cmp.not-eq] * `a != b` is the same as `!(a == b)` + +r[type.bool.expr.cmp.greater-eq] * `a >= b` is the same as `a == b | a > b` + +r[type.bool.expr.cmp.less] * `a < b` is the same as `!(a >= b)` + +r[type.bool.expr.cmp.less-eq] * `a <= b` is the same as `a == b | a < b` ## Bit validity +r[type.bool.validity] + The single byte of a `bool` is guaranteed to be initialized (in other words, `transmute::(...)` is always sound -- but since some bit patterns are invalid `bool`s, the inverse is not always sound). diff --git a/src/types/closure.md b/src/types/closure.md index f93537819..0f59f3f64 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -1,5 +1,7 @@ # Closure types +r[type.closure] + A [closure expression] produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured variables. For instance, the following @@ -47,6 +49,9 @@ f(Closure{s: s, t: &t}); ## Capture modes +r[type.closure.capture] + +r[type.closure.capture.order] The compiler prefers to capture a closed-over variable by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally by move. It will pick the first choice of these that is compatible with how the @@ -54,14 +59,17 @@ captured variable is used inside the closure body. The compiler does not take surrounding code into account, such as the lifetimes of involved variables, or of the closure itself. +r[type.closure.capture.move] If the `move` keyword is used, then all captures are by move or, for `Copy` types, by copy, regardless of whether a borrow would work. The `move` keyword is usually used to allow the closure to outlive the captured values, such as if the closure is being returned or used to spawn a new thread. +r[type.closure.capture.composite] Composite types such as structs, tuples, and enums are always captured entirely, not by individual fields. It may be necessary to borrow into a local variable in order to capture a single field: + ```rust # use std::collections::HashSet; @@ -87,6 +95,8 @@ borrowed to iterate over, the code would not compile. ## Unique immutable borrows in captures +r[type.closure.unique-immutable] + Captures can occur by a special kind of borrow called a _unique immutable borrow_, which cannot be used anywhere else in the language and cannot be written out explicitly. It occurs when modifying the referent of a mutable @@ -115,13 +125,18 @@ the closure's lifetime has expired at the end of the block, releasing the borrow ## Call traits and coercions +r[type.closure.call] + +r[type.closure.call.intro] Closure types all implement [`FnOnce`], indicating that they can be called once by consuming ownership of the closure. Additionally, some closures implement more specific call traits: +r[type.closure.call.fn-mut] * A closure which does not move out of any captured variables implements [`FnMut`], indicating that it can be called by mutable reference. +r[type.closure.call.fn] * A closure which does not mutate or move out of any captured variables implements [`Fn`], indicating that it can be called by shared reference. @@ -130,6 +145,7 @@ more specific call traits: > closure type are determined by what the closure does with captured values, > not how it captures them. +r[type.closure.non-capturing] *Non-capturing closures* are closures that don't capture anything from their environment. They can be coerced to function pointers (e.g., `fn()`) with the matching signature. @@ -146,6 +162,9 @@ x = bo(5,7); ## Other traits +r[type.closure.traits] + +r[type.closure.traits.intro] All closure types implement [`Sized`]. Additionally, closure types implement the following traits if allowed to do so by the types of the captures it stores: @@ -154,6 +173,7 @@ following traits if allowed to do so by the types of the captures it stores: * [`Sync`] * [`Send`] +r[type.closure.traits.behavior] The rules for [`Send`] and [`Sync`] match those for normal struct types, while [`Clone`] and [`Copy`] behave as if [derived]. For [`Clone`], the order of cloning of the captured variables is left unspecified. diff --git a/src/types/enum.md b/src/types/enum.md index 1ee6fc608..a3ae2878f 100644 --- a/src/types/enum.md +++ b/src/types/enum.md @@ -1,17 +1,24 @@ # Enumerated types +r[type.enum] + +r[type.enum.intro] An *enumerated type* is a nominal, heterogeneous disjoint union type, denoted by the name of an [`enum` item]. [^enumtype] +r[type.enum.declaration] An [`enum` item] declares both the type and a number of *variants*, each of which is independently named and has the syntax of a struct, tuple struct or unit-like struct. +r[type.enum.constructor] New instances of an `enum` can be constructed with a [struct expression]. +r[type.enum.value] Any `enum` value consumes as much memory as the largest variant for its corresponding `enum` type, as well as the size needed to store a discriminant. +r[type.enum.name] Enum types cannot be denoted *structurally* as types, but must be denoted by named reference to an [`enum` item]. diff --git a/src/types/function-item.md b/src/types/function-item.md index 3221f3e2b..3aa5a24e2 100644 --- a/src/types/function-item.md +++ b/src/types/function-item.md @@ -1,13 +1,19 @@ # Function item types +r[type.fn-item] + +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_. That type -explicitly identifies the function - its name, its type arguments, and its +enum variant, yields a zero-sized value of its _function item type_\1 + +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. +r[type.fn-item.name] There is no syntax that directly refers to a function item type, but the compiler will display the type as something like `fn(u32) -> i32 {fn_name}` in error messages. @@ -22,6 +28,7 @@ let x = &mut foo::; *x = foo::; //~ ERROR mismatched types ``` +r[type.fn-item.coercion] However, there is a [coercion] from function items to [function pointers] with the same signature, which is triggered not only when a function item is used when a function pointer is directly expected, but also when different function @@ -43,6 +50,7 @@ let foo_ptr_2 = if want_i32 { }; ``` +r[type.fn-item.traits] All function items implement [`Fn`], [`FnMut`], [`FnOnce`], [`Copy`], [`Clone`], [`Send`], and [`Sync`]. diff --git a/src/types/function-pointer.md b/src/types/function-pointer.md index 82103beaa..3fbe207f9 100644 --- a/src/types/function-pointer.md +++ b/src/types/function-pointer.md @@ -1,5 +1,8 @@ # Function pointer types +r[type.fn-pointer] + +r[type.fn-pointer.syntax] > **Syntax**\ > _BareFunctionType_ :\ >    [_ForLifetimes_]? _FunctionTypeQualifiers_ `fn`\ @@ -23,13 +26,18 @@ > _MaybeNamedFunctionParametersVariadic_ :\ >    ( _MaybeNamedParam_ `,` )\* _MaybeNamedParam_ `,` [_OuterAttribute_]\* `...` +r[type.fn-pointer.intro] Function pointer types, written using the `fn` keyword, refer to a function -whose identity is not necessarily known at compile-time. They can be created -via a coercion from both [function items] and non-capturing [closures]. +whose identity is not necessarily known at compile-time. + +r[type.fn-pointer.coercion] +They can be created via a coercion from both [function items] and non-capturing [closures]. +r[type.fn-pointer.qualifiers] The `unsafe` qualifier indicates that the type's value is an [unsafe function], and the `extern` qualifier indicates it is an [extern function]. +r[type.fn-pointer.constraint-variadic] Variadic parameters can only be specified with [`extern`] function types with the `"C"` or `"cdecl"` calling convention. @@ -49,6 +57,8 @@ x = bo(5,7); ## Attributes on function pointer parameters +r[type.fn-pointer.attributes] + Attributes on function pointer parameters follow the same rules and restrictions as [regular function parameters]. diff --git a/src/types/impl-trait.md b/src/types/impl-trait.md index 7e99949cf..5a913344a 100644 --- a/src/types/impl-trait.md +++ b/src/types/impl-trait.md @@ -1,10 +1,14 @@ # Impl trait +r[type.impl-trait] + +r[type.impl-trait.syntax] > **Syntax**\ > _ImplTraitType_ : `impl` [_TypeParamBounds_] > > _ImplTraitTypeOneBound_ : `impl` [_TraitBound_] +r[type.impl-trait.intro] `impl Trait` provides ways to specify unnamed but concrete types that implement a specific trait. It can appear in two sorts of places: argument position (where it can act as an anonymous type parameter to functions), and return position (where it can act as an abstract return type). @@ -23,9 +27,12 @@ fn bar() -> impl Trait { ``` ## Anonymous type parameters +r[type.impl-trait.param] + > Note: This is often called "impl Trait in argument position". (The term "parameter" is more correct here, but "impl Trait in argument position" is the phrasing used during the development of this feature, and it remains in parts of the implementation.) +r[type.impl-trait.param.intro] Functions can use `impl` followed by a set of trait bounds to declare a parameter as having an anonymous type. The caller must provide a type that satisfies the bounds declared by the anonymous type parameter, and the function can only use the methods available through the trait bounds of the anonymous type parameter. @@ -43,6 +50,7 @@ fn with_impl_trait(arg: impl Trait) { } ``` +r[type.impl-trait.param.generic] That is, `impl Trait` in argument position is syntactic sugar for a generic type parameter like ``, except that the type is anonymous and doesn't appear in the [_GenericParams_] list. > **Note:** @@ -52,10 +60,15 @@ That is, `impl Trait` in argument position is syntactic sugar for a generic type ## Abstract return types +r[type.impl-trait.return] + > Note: This is often called "impl Trait in return position". +r[type.impl-trait.return.intro] Functions can use `impl Trait` to return an abstract return type. These types stand in for another concrete type where the caller may only use the methods declared by the specified `Trait`. + +r[type.impl-trait.return.constraint-body] Each possible return value from the function must resolve to the same concrete type. `impl Trait` in return position allows a function to return an unboxed abstract type. @@ -87,22 +100,38 @@ Returning `impl Iterator` means that a function only exposes the `Iterator` trai ## Return-position `impl Trait` in traits and trait implementations +r[type.impl-trait.return-in-trait] + +r[type.impl-trait.return-in-trait.intro] Functions in traits may also use `impl Trait` as a syntax for an anonymous associated type. +r[type.impl-trait.return-in-trait.desugaring] Every `impl Trait` in the return type of an associated function in a trait is desugared to an anonymous associated type. The return type that appears in the implementation's function signature is used to determine the value of the associated type. ## Capturing +r[type.impl-trait.generic-captures] + Behind each return-position `impl Trait` abstract type is some hidden concrete type. For this concrete type to use a generic parameter, that generic parameter must be *captured* by the abstract type. ## Automatic capturing +r[type.impl-trait.generic-capture.auto] + +r[type.impl-trait.generic-capture.auto.intro] Return-position `impl Trait` abstract types automatically capture certain of the in-scope generic parameters. Everywhere, these automatically capture all in-scope type and const generic parameters. -On items of trait impls and trait definitions, these types additionally automatically capture all in-scope generic lifetime parameters, including higher-ranked ones. On free functions and on associated functions and methods of inherent impls, only the generic lifetime parameters that appear in the bounds of abstract return type are captured. +r[type.impl-trait.generic-capture.auto.trait] +On items of trait impls and trait definitions, these types additionally automatically capture all in-scope generic lifetime parameters, including higher-ranked ones\1 + +r[type.impl-trait.generic-capture.auto.fn] +On free functions and on associated functions and methods of inherent impls, only the generic lifetime parameters that appear in the bounds of abstract return type are captured. ## Precise capturing +r[type.impl-trait.generic-capture.precise] + +r[type.impl-trait.generic-capture.precise.use] The set of generic parameters captured by a return-position `impl Trait` abstract type may be explicitly controlled with a [`use<..>` bound]. If present, only the generic parameters listed in the `use<..>` bound will be captured. E.g.: ```rust @@ -113,8 +142,13 @@ fn capture<'a, 'b, T>(x: &'a (), y: T) -> impl Sized + use<'a, T> { } ``` -Currently, only one `use<..>` bound may be present in a bounds list, such bounds are not allowed in the signature of items of a trait definition, all in-scope type and const generic parameters must be included, and all lifetime parameters that appear in other bounds of the abstract type must be included. Within the `use<..>` bound, any lifetime parameters present must appear before all type and const generic parameters, and the elided lifetime (`'_`) may be present if it is otherwise allowed to appear within the `impl Trait` return type. +r[type.impl-trait.generic-capture.precise.constraint-single] +Currently, only one `use<..>` bound may be present in a bounds list, such bounds are not allowed in the signature of items of a trait definition, all in-scope type and const generic parameters must be included, and all lifetime parameters that appear in other bounds of the abstract type must be included\1 +r[type.impl-trait.generic-capture.precise.constraint-lifetime] +Within the `use<..>` bound, any lifetime parameters present must appear before all type and const generic parameters, and the elided lifetime (`'_`) may be present if it is otherwise allowed to appear within the `impl Trait` return type. + +r[type.impl-trait.generic-capture.precise.constraint-param-impl-trait] Because all in-scope type parameters must be included by name, a `use<..>` bound may not be used in the signature of items that use argument-position `impl Trait`, as those items have anonymous type parameters in scope. ## Differences between generics and `impl Trait` in return position @@ -150,6 +184,8 @@ Instead, the function chooses the return type, but only promises that it will im ## Limitations +r[type.impl-trait.constraint] + `impl Trait` can only appear as a parameter or return type of a non-`extern` function. It cannot be the type of a `let` binding, field type, or appear inside a type alias. diff --git a/src/types/inferred.md b/src/types/inferred.md index c33ebd91c..7179826c4 100644 --- a/src/types/inferred.md +++ b/src/types/inferred.md @@ -1,11 +1,19 @@ # Inferred type +r[type.inferred] + +r[type.inferred.syntax] > **Syntax**\ > _InferredType_ : `_` +r[type.inferred.intro] The inferred type asks the compiler to infer the type if possible based on the -surrounding information available. It cannot be used in item signatures. It is -often used in generic arguments: +surrounding information available. + +r[type.inferred.constraint] +It cannot be used in item signatures. + +It is often used in generic arguments: ```rust let x: Vec<_> = (0..10).collect(); diff --git a/src/types/never.md b/src/types/never.md index 7f58a3ace..702281db2 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -1,12 +1,19 @@ # Never type +r[type.never] + +r[type.never.syntax] > **Syntax**\ > _NeverType_ : `!` +r[type.never.intro] The never type `!` is a type with no values, representing the result of -computations that never complete. Expressions of type `!` can be coerced into -any other type. +computations that never complete. + +r[type.never.coercion] +Expressions of type `!` can be coerced into any other type. +r[type.never.constraint] The `!` type can **only** appear in function return types presently, indicating it is a diverging function that never returns. diff --git a/src/types/numeric.md b/src/types/numeric.md index bd59daa6b..88178d123 100644 --- a/src/types/numeric.md +++ b/src/types/numeric.md @@ -1,7 +1,12 @@ # Numeric types +r[type.numeric] + ## Integer types +r[type.numeric.int] + +r[type.numeric.int.unsigned] The unsigned integer types consist of: Type | Minimum | Maximum @@ -12,6 +17,7 @@ Type | Minimum | Maximum `u64` | 0 | 264-1 `u128` | 0 | 2128-1 +r[type.numeric.int.signed] The signed two's complement integer types consist of: Type | Minimum | Maximum @@ -25,20 +31,27 @@ Type | Minimum | Maximum ## Floating-point types +r[type.numeric.float] + The IEEE 754-2008 "binary32" and "binary64" floating-point types are `f32` and `f64`, respectively. ## Machine-dependent integer types +r[type.numeric.int.size] + +r[type.numeric.int.size.usize] The `usize` type is an unsigned integer type with the same number of bits as the platform's pointer type. It can represent every memory address in the process. +r[type.numeric.int.size.isize] The `isize` type is a signed integer type with the same number of bits as the platform's pointer type. The theoretical upper bound on object and array size is the maximum `isize` value. This ensures that `isize` can be used to calculate differences between pointers into an object or array and can address every byte within an object along with one byte past the end. +r[type.numeric.int.size.minimum] `usize` and `isize` are at least 16-bits wide. > **Note**: Many pieces of Rust code may assume that pointers, `usize`, and @@ -48,5 +61,7 @@ within an object along with one byte past the end. ## Bit validity +r[type.numeric.validity] + For every numeric type, `T`, the bit validity of `T` is equivalent to the bit validity of `[u8; size_of::()]`. An uninitialized byte is not a valid `u8`. diff --git a/src/types/parameters.md b/src/types/parameters.md index 7b9e7e64e..89b7df9ec 100644 --- a/src/types/parameters.md +++ b/src/types/parameters.md @@ -1,5 +1,7 @@ # Type parameters +r[type.generic] + Within the body of an item that has type parameter declarations, the names of its type parameters are types: diff --git a/src/types/pointer.md b/src/types/pointer.md index 7299ce75e..0f24d6bce 100644 --- a/src/types/pointer.md +++ b/src/types/pointer.md @@ -1,61 +1,96 @@ # Pointer types +r[type.pointer] + +r[type.pointer.intro] All pointers are explicit first-class values. They can be moved or copied, stored into data structs, and returned from functions. ## References (`&` and `&mut`) +r[type.pointer.reference] + +r[type.pointer.reference.syntax] > **Syntax**\ > _ReferenceType_ :\ >    `&` [_Lifetime_]? `mut`? [_TypeNoBounds_] ### Shared references (`&`) +r[type.pointer.reference.shared] + +r[type.pointer.reference.shared.intro] Shared references point to memory which is owned by some other value. + +r[type.pointer.reference.shared.constraint-mutation] When a shared reference to a value is created, it prevents direct mutation of the value. [Interior mutability] provides an exception for this in certain circumstances. As the name suggests, any number of shared references to a value may exist. A shared reference type is written `&type`, or `&'a type` when you need to specify an explicit lifetime. + +r[type.pointer.reference.shared.copy] Copying a reference is a "shallow" operation: it involves only copying the pointer itself, that is, pointers are `Copy`. Releasing a reference has no effect on the value it points to, but referencing of a [temporary value] will keep it alive during the scope of the reference itself. ### Mutable references (`&mut`) +r[type.pointer.reference.mut] + +r[type.pointer.reference.mut.intro] Mutable references point to memory which is owned by some other value. A mutable reference type is written `&mut type` or `&'a mut type`. + +r[type.pointer.reference.mut.copy] A mutable reference (that hasn't been borrowed) is the only way to access the value it points to, so is not `Copy`. ## Raw pointers (`*const` and `*mut`) +r[type.pointer.raw] + +r[type.pointer.raw.syntax] > **Syntax**\ > _RawPointerType_ :\ >    `*` ( `mut` | `const` ) [_TypeNoBounds_] +r[type.pointer.raw.intro] Raw pointers are pointers without safety or liveness guarantees. Raw pointers are written as `*const T` or `*mut T`. For example `*const i32` means a raw pointer to a 32-bit integer. + +r[type.pointer.raw.copy] Copying or dropping a raw pointer has no effect on the lifecycle of any other value. + +r[type.pointer.raw.safety] Dereferencing a raw pointer is an [`unsafe` operation]. + This can also be used to convert a raw pointer to a reference by reborrowing it (`&*` or `&mut *`). Raw pointers are generally discouraged; they exist to support interoperability with foreign code, and writing performance-critical or low-level functions. +r[type.pointer.raw.cmp] When comparing raw pointers they are compared by their address, rather than by what they point to. When comparing raw pointers to [dynamically sized types] they also have their additional data compared. +r[type.pointer.raw.constructor] Raw pointers can be created directly using `&raw const` for `*const` pointers and `&raw mut` for `*mut` pointers. ## Smart Pointers +r[type.pointer.smart] + The standard library contains additional 'smart pointer' types beyond references and raw pointers. ## Bit validity +r[type.pointer.validity] + +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::

()]`. +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). diff --git a/src/types/slice.md b/src/types/slice.md index 6ba5e7d21..79c340a87 100644 --- a/src/types/slice.md +++ b/src/types/slice.md @@ -1,12 +1,17 @@ # Slice types +r[type.slice] + +r[type.slice.syntax] > **Syntax**\ > _SliceType_ :\ >    `[` [_Type_] `]` +r[type.slice.intro] A slice is a [dynamically sized type] representing a 'view' into a sequence of elements of type `T`. The slice type is written as `[T]`. +r[type.slice.unsized] Slice types are generally used through pointer types. For example: * `&[T]`: a 'shared slice', often just called a 'slice'. It doesn't own the @@ -24,6 +29,7 @@ let boxed_array: Box<[i32]> = Box::new([1, 2, 3]); let slice: &[i32] = &boxed_array[..]; ``` +r[type.slice.safe] All elements of slices are always initialized, and access to a slice is always bounds-checked in safe methods and operators. diff --git a/src/types/struct.md b/src/types/struct.md index 1f20dbb3c..6a672f7af 100644 --- a/src/types/struct.md +++ b/src/types/struct.md @@ -1,22 +1,30 @@ # Struct types +r[type.struct] + +r[type.struct.intro] A `struct` *type* is a heterogeneous product of other types, called the *fields* of the type.[^structtype] +r[type.struct.constructor] New instances of a `struct` can be constructed with a [struct expression]. +r[type.struct.layout] The memory layout of a `struct` is undefined by default to allow for compiler optimizations like field reordering, but it can be fixed with the [`repr` attribute]. In either case, fields may be given in any order in a corresponding struct *expression*; the resulting `struct` value will always have the same memory layout. +r[type.struct.field-visibility] The fields of a `struct` may be qualified by [visibility modifiers], to allow access to data in a struct outside a module. +r[type.struct.tuple] A _tuple struct_ type is just like a struct type, except that the fields are anonymous. +r[type.struct.unit] A _unit-like struct_ type is like a struct type, except that it has no fields. The one value constructed by the associated [struct expression] is the only value that inhabits such a type. diff --git a/src/types/textual.md b/src/types/textual.md index a4765b523..e8515338c 100644 --- a/src/types/textual.md +++ b/src/types/textual.md @@ -1,26 +1,39 @@ # Textual types +r[type.text] + +r[type.text.intro] The types `char` and `str` hold textual data. +r[type.text.char-value] A value of type `char` is a [Unicode scalar value] (i.e. a code point that is not a surrogate), represented as a 32-bit unsigned word in the 0x0000 to 0xD7FF -or 0xE000 to 0x10FFFF range. It is immediate [undefined behavior] to create a +or 0xE000 to 0x10FFFF range. + +r[type.text.char-precondition] +It is immediate [undefined behavior] to create \1 `char` that falls outside this range. A `[char]` is effectively a UCS-4 / UTF-32 string of length 1. +r[type.text.str-value] A value of type `str` is represented the same way as `[u8]`, a slice of 8-bit unsigned bytes. However, the Rust standard library makes extra assumptions about `str`: methods working on `str` assume and ensure that the data in there is valid UTF-8. Calling a `str` method with a non-UTF-8 buffer can cause [undefined behavior] now or in the future. +r[type.text.str-unsized] Since `str` is a [dynamically sized type], it can only be instantiated through a pointer type, such as `&str`. ## Layout and bit validity +r[type.text.layout] + +r[type.layout.char-layout] `char` is guaranteed to have the same size and alignment as `u32` on all platforms. +r[type.layout.char-validity] Every byte of a `char` is guaranteed to be initialized (in other words, `transmute::()]>(...)` is always sound -- but since some bit patterns are invalid `char`s, the inverse is not always sound). diff --git a/src/types/trait-object.md b/src/types/trait-object.md index 5b8541fa8..598ad290b 100644 --- a/src/types/trait-object.md +++ b/src/types/trait-object.md @@ -1,5 +1,8 @@ # Trait objects +r[type.trait-object] + +r[type.trait-object.syntax] > **Syntax**\ > _TraitObjectType_ :\ >    `dyn`? [_TypeParamBounds_] @@ -7,16 +10,21 @@ > _TraitObjectTypeOneBound_ :\ >    `dyn`? [_TraitBound_] +r[type.trait-object.intro] A *trait object* is an opaque value of another type that implements a set of traits. The set of traits is made up of an [object safe] *base trait* plus any number of [auto traits]. +r[type.trait-object.impls] Trait objects implement the base trait, its auto traits, and any [supertraits] of the base trait. +r[type.trait-object.name] Trait objects are written as the keyword `dyn` followed by a set of trait -bounds, but with the following restrictions on the trait bounds. All traits -except the first trait must be auto traits, there may not be more than one +bounds, but with the following restrictions on the trait bounds. + +r[type.trait-object.constraint] +All traits except the first trait must be auto traits, there may not be more than one lifetime, and opt-out bounds (e.g. `?Sized`) are not allowed. Furthermore, paths to traits may be parenthesized. @@ -31,12 +39,14 @@ For example, given a trait `Trait`, the following are all trait objects: * `dyn 'static + Trait`. * `dyn (Trait)` +r[type.trait-object.syntax-edition2021] > **Edition differences**: Before the 2021 edition, the `dyn` keyword may be > omitted. > > Note: For clarity, it is recommended to always use the `dyn` keyword on your > trait objects unless your codebase supports compiling with Rust 1.26 or lower. +r[type.trait-object.syntax-edition2015] > **Edition differences**: In the 2015 edition, if the first bound of the > trait object is a path that starts with `::`, then the `dyn` will be treated > as a part of the path. The first path can be put in parenthesis to get @@ -46,11 +56,13 @@ For example, given a trait `Trait`, the following are all trait objects: > Beginning in the 2018 edition, `dyn` is a true keyword and is not allowed in > paths, so the parentheses are not necessary. +r[type.trait-object.alias] Two trait object types alias each other if the base traits alias each other and if the sets of auto traits are the same and the lifetime bounds are the same. For example, `dyn Trait + Send + UnwindSafe` is the same as `dyn Trait + UnwindSafe + Send`. +r[type.trait-object.unsized] Due to the opaqueness of which concrete type the value is of, trait objects are [dynamically sized types]. Like all DSTs, trait objects are used @@ -93,6 +105,8 @@ type signature of `print`, and the cast expression in `main`. ## Trait Object Lifetime Bounds +r[type.trait-object.lifetime-bounds] + Since a trait object can contain references, the lifetimes of those references need to be expressed as part of the trait object. This lifetime is written as `Trait + 'a`. There are [defaults] that allow this lifetime to usually be diff --git a/src/types/tuple.md b/src/types/tuple.md index 804d8a0ae..073fbd193 100644 --- a/src/types/tuple.md +++ b/src/types/tuple.md @@ -1,26 +1,35 @@ # Tuple types +r[type.tuple] + +r[type.tuple.syntax] > **Syntax**\ > _TupleType_ :\ >       `(` `)`\ >    | `(` ( [_Type_] `,` )+ [_Type_]? `)` +r[type.tuple.intro] *Tuple types* are a family of structural types[^1] for heterogeneous lists of other types. The syntax for a tuple type is a parenthesized, comma-separated list of types. + +r[type.tuple.restriction] 1-ary tuples require a comma after their element type to be disambiguated with a [parenthesized type]. +r[type.tuple.field-number] A tuple type has a number of fields equal to the length of the list of types. This number of fields determines the *arity* of the tuple. A tuple with `n` fields is called an *n-ary tuple*. For example, a tuple with 2 fields is a 2-ary tuple. +r[type.tuple.field-name] Fields of tuples are named using increasing numeric names matching their position in the list of types. The first field is `0`. The second field is `1`. And so on. The type of each field is the type of the same position in the tuple's list of types. +r[type.tuple.unit] For convenience and historical reasons, the tuple type with no fields (`()`) is often called *unit* or *the unit type*. Its one value is also called *unit* or *the unit value*. @@ -33,8 +42,11 @@ Some examples of tuple types: * `(i32, String)` (different type from the previous example) * `(i32, f64, Vec, Option)` +r[type.tuple.constructor] Values of this type are constructed using a [tuple expression]. Furthermore, various expressions will produce the unit value if there is no other meaningful value for it to evaluate to. + +r[type.tuple.access] Tuple fields can be accessed by either a [tuple index expression] or [pattern matching]. [^1]: Structural types are always equivalent if their internal types are equivalent. diff --git a/src/types/union.md b/src/types/union.md index 7a2f037e8..c8801ee2f 100644 --- a/src/types/union.md +++ b/src/types/union.md @@ -1,15 +1,25 @@ # Union types +r[type.union] + +r[type.union.intro] A *union type* is a nominal, heterogeneous C-like union, denoted by the name of a [`union` item][item]. +r[type.union.access] Unions have no notion of an "active field". Instead, every union access transmutes parts of the content of the union to the type of the accessed field. + +r[type.union.safety] Since transmutes can cause unexpected or undefined behaviour, `unsafe` is -required to read from a union field. Union field types are also restricted to a +required to read from a union field. + +r[type.union.constraint] +Union field types are also restricted to a subset of types which ensures that they never need dropping. See the [item] documentation for further details. +r[type.union.layout] The memory layout of a `union` is undefined by default (in particular, fields do *not* have to be at offset 0), but the `#[repr(...)]` attribute can be used to fix a layout.