Caution

You’re reading a draft of the Ferrocene Language Specification. Some parts of this document might be missing, incomplete or incorrect. Our aim is to have the specification ready by the end of 2022.

4. Types and Traits

4.1. Types

Syntax

TypeSpecification ::=
    ImplTraitTypeSpecification
  | TraitObjectTypeSpecification
  | TypeSpecificationWithoutBounds

TypeSpecificationList ::=
    TypeSpecification (, TypeSpecification)* ,?

TypeSpecificationWithoutBounds ::=
    ArrayTypeSpecification
  | FunctionPointerTypeSpecification
  | ImplTraitTypeSpecificationOneBound
  | InferredType
  | MacroInvocation
  | NeverType
  | ParenthesizedTypeSpecification
  | QualifiedPathInType
  | RawPointerTypeSpecification
  | ReferenceTypeSpecification
  | SliceTypeSpecification
  | TraitObjectTypeSpecificationOneBound
  | TupleTypeSpecification
  | TypePath

TypeAscription ::=
    : TypeSpecification

Legality Rules

4.1:1 A type defines a set of values and a set of operations that act on those values.

4.1:2 A local type is a type that is defined in the current crate.

4.2. Type Classification

Note

The contents of this section are informational.

Legality Rules

4.2:1 Types are organized in the following categories:

4.3. Scalar Types

4.3.1. Bool Type

Legality Rules

4.3.1:1 Bool is a type whose values denote the truth values of logic and Boolean algebra.

4.3.1:2 Type bool appears in the language prelude under the name bool.

4.3.1:3 Boolean value false has bit pattern 0x00. Boolean value true has bit pattern 0x01.

4.3.1:4 The following operations are defined on type bool:

4.3.1:5 Logical not

4.3.1:6

a

!a

4.3.1:7

true

false

4.3.1:8

false

true

4.3.1:9 Logical and

4.3.1:10

a

b

a & b

4.3.1:11

true

true

true

4.3.1:12

true

false

false

4.3.1:13

false

true

false

4.3.1:14

false

false

false

4.3.1:15 Logical or

4.3.1:16

a

b

a | b

4.3.1:17

true

true

true

4.3.1:18

true

false

true

4.3.1:19

false

true

true

4.3.1:20

false

false

false

4.3.1:21 Logical exclusive or (xor)

4.3.1:22

a

b

a ^ b

4.3.1:23

true

true

false

4.3.1:24

true

false

true

4.3.1:25

false

true

true

4.3.1:26

false

false

false

4.3.1:27 Equality

4.3.1:28

a

b

a == b

4.3.1:29

true

true

true

4.3.1:30

true

false

false

4.3.1:31

false

true

false

4.3.1:32

false

false

true

4.3.1:33 Greater than

4.3.1:34

a

b

a > b

4.3.1:35

true

true

false

4.3.1:36

true

false

true

4.3.1:37

false

true

false

4.3.1:38

false

false

false

4.3.1:39 Operation a != b is equivalent to !(a == b).

4.3.1:40 Operation a >= b is equivalent to a == b | a > b.

4.3.1:41 Operation a < b is equivalent to !(a >= b).

4.3.1:42 Operation a <= b shall be equivalent to a == b | a < b.

Undefined Behavior

4.3.1:43 It is a validity invariant for a value of type bool to have a bit pattern of 0x00 and 0x01.

4.3.2. Char Type

Legality Rules

4.3.2:1 Char is a type whose values are represented as a 32-bit unsigned word in the 0x000 to 0xD7FF or the 0xE000 to 0x10FFFF inclusive ranges of Unicode.

Undefined Behavior

4.3.2:2 It is a validity invariant for a value of type char to be inside the 0x000 to 0xD7FF or the 0xE000 to 0x10FFFF inclusive ranges of Unicode.

4.3.3. Numeric Types

4.3.3.1. Floating Point Types

Legality Rules

4.3.3.1:1 Type f32 is equivalent to the IEEE 754-2008 binary32 type.

4.3.3.1:2 Type f64 is equivalent to the IEEE 754-2008 binary64 type.

4.3.3.2. Integer Types

Legality Rules

4.3.3.2:1 Unsigned integer types define the following inclusive ranges over the domain of whole numbers:

4.3.3.2:2

Type

Minimum

Maximum

4.3.3.2:3

u8

0

28 - 1

4.3.3.2:4

u16

0

216 - 1

4.3.3.2:5

u32

0

232 - 1

4.3.3.2:6

u64

0

264 - 1

4.3.3.2:7

u128

0

2128 - 1

4.3.3.2:8 Type usize has the same number of bits as the platform’s pointer type, and at least 16-bits wide.

4.3.3.2:9 Signed integer types define the following inclusive ranges over the domain of whole numbers:

4.3.3.2:10

Type

Minimum

Maximum

4.3.3.2:11

i8

- (27)

27 - 1

4.3.3.2:12

i16

- (215)

215 - 1

4.3.3.2:13

i32

- (231)

231 - 1

4.3.3.2:14

i64

- (263)

263 - 1

4.3.3.2:15

i128

- (2127)

2127 - 1

4.3.3.2:16 Type isize has the same number of bits as the platform’s pointer type, and at least 16-bits wide.

4.4. Sequence Types

4.4.1. Array Types

Syntax

ArrayTypeSpecification ::=
    [ ElementType ; SizeOperand ]

ElementType ::=
    TypeSpecification

Legality Rules

4.4.1:1 An array type is a sequence type that represents a fixed sequence of elements.

4.4.1:2 The element type shall be a fixed sized type.

4.4.1:3 The size operand shall be a constant expression.

4.4.1:4 The type of the size operand is type usize.

Examples

4.4.1:5 An array type in the context of a let statement:

let array: [i32; 3] = [1, 2, 3];

4.4.2. Slice Types

Syntax

SliceTypeSpecification ::=
    [ ElementType ]

Legality Rules

4.4.2:1 A slice type is a sequence type that provides a view into a sequence of elements.

4.4.2:2 The element type shall be a fixed sized type.

4.4.2:3 A slice type is a dynamically sized type.

Examples

4.4.2:4 See Paragraph 4.3.1. for the declaration of array.

let slice: &[i32] = &array[0..1];

4.4.3. Str Type

Legality Rules

4.4.3:1 Str is a sequence type that represents a slice of 8-bit unsigned bytes.

4.4.3:2 Type str is a dynamically sized type.

4.4.3:3 A value of type str shall denote a valid UTF-8 sequence of characters.

Undefined Behavior

4.4.3:4 It is a safety invariant for a value of type str to denote a valid UTF-8 sequence of characters.

4.4.4. Tuple Types

Syntax

TupleTypeSpecification ::=
    ( TupleFieldList? )

TupleFieldList ::=
    TupleField (, TupleField)* ,?

TupleField ::=
    TypeSpecification

Legality Rules

4.4.4:1 A tuple type is a sequence type that represents a heterogeneous list of other types.

4.4.4:2 If the type of a tuple field is a dynamically-sized type, then the tuple field shall be the last tuple field in the TupleFieldList.

Examples

()
(char,)
(i32, f64, Vec<String>)

4.5. Abstract Data Types

4.5.1. Enum Types

Syntax

EnumDeclaration ::=
    enum Name GenericParameterList? WhereClause? { EnumVariantList? }

EnumVariantList ::=
    EnumVariant (, EnumVariant)* ,?

EnumVariant ::=
    OuterAttributeOrDoc* VisibilityModifier? Name EnumVariantKind?

EnumVariantKind ::=
    DiscriminantInitializer
  | RecordStructFieldList
  | TupleStructFieldList

DiscriminantInitializer ::=
    = Expression

Legality Rules

4.5.1:1 An enum type is an abstract data type that contains enum variants.

4.5.1:2 A zero-variant enum type has no values.

4.5.1:3 An enum variant is a construct that declares one of the possible variations of an enum.

4.5.1:4 The name of an enum variant shall denote a unique name within the related EnumDeclaration.

B.159:1 A discriminant is an opaque integer that identifies an enum variant.

4.5.1:6 A discriminant initializer shall be specified only when all enum variants appear without an EnumVariantKind.

4.5.1:7 The type of the expression of a discriminant initializer shall be either:

4.5.1:10 The value of the expression of a discriminant initializer shall be a constant expression.

4.5.1:11 The value of a discriminant of an enum variant is determined as follows:

  1. 4.5.1:12 If the enum variant contains a discriminant initializer, then the value is the value of its expression.

  2. 4.5.1:13 Else, if the enum variant is the first enum variant in the EnumVariantList, then the value is zero.

  3. 4.5.1:14 Otherwise the value is one greater than the value of the discriminant of the previous enum variant.

4.5.1:15 It is a static error if two enum variants have the same discriminants with the same value.

4.5.1:16 It is a static error if the value of a discriminant exceeds the maximum value of the type of the expression of a discriminant initializer.

Undefined Behavior

4.5.1:17 It is a validity invariant for a value of an enum type to have a discriminant specified by the enum type.

Examples

enum ZeroVariantEnumType {}

enum Animal {
   Cat,
   Dog(String),
   Otter { name: String, weight: f64, age: u8 }
}

enum Discriminants {
    First,       // The discriminant is 0.
    Second,      // The discriminant is 1.
    Third = 12,  // The discriminant is 12.
    Fourth,      // The discriminant is 13.
    Fifth = 34,  // The discriminant is 34.
    Sixth        // The discriminant is 35.
}

4.5.2. Struct Types

Syntax

StructDeclaration ::=
    RecordStructDeclaration
  | TupleStructDeclaration
  | UnitStructDeclaration

RecordStructDeclaration ::=
    struct Name GenericParameterList? WhereClause? RecordStructFieldList

RecordStructFieldList ::=
    { (RecordStructField (, RecordStructField)* ,?)? }

RecordStructField ::=
    OuterAttributeOrDoc* VisibilityModifier? Name TypeAscription

TupleStructDeclaration ::=
    struct Name GenericParameterList? TupleStructFieldList WhereClause? ;

TupleStructFieldList ::=
    ( (TupleStructField (, TupleStructField)* ,?)? )

TupleStructField ::=
    OuterAttributeOrDoc* VisibilityModifier? TypeSpecification

UnitStructDeclaration ::=
    struct Name GenericParameterList? WhereClause? ;

Legality Rules

4.5.2:1 A struct type is an abstract data type that is a product of other types.

4.5.2:2 The name of a record struct field shall denote a unique name within the RecordStructDeclaration.

4.5.2:3 If the type of a record struct field is a dynamically sized type, then the record struct field shall be the last record struct field in the RecordStructFieldList.

4.5.2:4 If the type of a tuple struct field is a dynamically sized type, then the tuple struct field shall be the last tuple struct field in the TupleStructFieldList.

Examples

struct UnitStruct;

struct AnimalRecordStruct {
    name: String,
    weight: f64,
    age: u8
}

struct AnimalTupleStruct (
    String,
    f64,
    u8
);

4.5.3. Union Types

Syntax

UnionDeclaration ::=
    union Name GenericParameterList? WhereClause? RecordStructFieldList

Legality Rules

4.5.3:1 A union type is an abstract data type similar to a C-like union.

4.5.3:2 The name of a union field shall denote a unique name within the related RecordStructDeclaration.

Examples

union LeafNode {
    int: i32,
    float: f32,
    double: f64
}

4.6. Function Types

4.6.1. Closure Types

Legality Rules

4.6.1:1 A closure type is a unique anonymous function type that encapsulates all capture targets of a closure expression.

4.6.1:2 A closure type implements the core::ops::FnOnce trait.

4.6.1:3 A closure type that does not move out its capture targets implements the core::ops::FnMut trait.

4.6.1:4 A closure type that does not move or mutate its capture targets implements the core::ops::Fn trait.

4.6.1:5 A closure type that does not encapsulate capture targets is coercible to a function pointer type.

4.6.1:6 A closure type implicitly implements the core::marker::Copy trait if

4.6.1:9 A closure type implicitly implements the core::clone::Clone trait if

4.6.1:12 A closure type implicitly implements the core::marker::Send trait if:

4.6.1:15 A closure type implicitly implements the core::marker::Sync trait if the types of all capture targets implement the core::marker::Sync trait.

4.6.2. Function Item Types

Legality Rules

4.6.2:1 A function item type is a unique anonymous function type that identifies a function.

4.6.2:2 An external function item type is a function item type where the related function is an external function.

4.6.2:3 An unsafe function item type is a function item type where the related function is an unsafe function.

4.6.2:4 A function item type is coercible to a function pointer type.

4.6.2:5 A function item type implements the core::ops::Fn trait, the core::ops::FnMut trait, the core::ops::FnOnce trait, the core::marker::Copy trait, the core::clone::Clone trait, the core::marker::Send trait, and the core::marker::Sync trait.

4.7. Indirection Types

4.7.1. Function Pointer Types

Syntax

FunctionPointerTypeSpecification ::=
    ForGenericParameterList? FunctionPointerTypeQualifierList fn
      ( FunctionPointerTypeParameterList? ) ReturnTypeWithoutBounds?

FunctionPointerTypeQualifierList ::=
    unsafe? AbiSpecification?

FunctionPointerTypeParameterList ::=
    FunctionPointerTypeParameter (, FunctionPointerTypeParameter)*
      (, VariadicPart | ,?)

VariadicPart ::=
    OuterAttributeOrDoc* ...

FunctionPointerTypeParameter ::=
    OuterAttributeOrDoc* (IdentifierOrUnderscore :)? TypeSpecification

Legality Rules

4.7.1:1 A function pointer type is an indirection type that refers to a function.

4.7.1:2 An unsafe function pointer type is a function pointer type subject to keyword unsafe.

4.7.1:3 A variadic part indicates the presence of C-like optional parameters.

4.7.1:4 A variadic part shall be specified only when the ABI of the function pointer type is either extern "C" or extern "cdecl".

Undefined Behavior

4.7.1:5 It is a validity invariant for a value of a function pointer type to be not null.

Examples

unsafe extern "C" fn (value: i32, ...) -> f64

4.7.2. Raw Pointer Types

Syntax

RawPointerTypeSpecification ::=
    * (const | mut) TypeSpecificationWithoutBounds

Legality Rules

4.7.2:1 A raw pointer type is an indirection type without validity guarantees.

4.7.2:2 A mutable raw pointer type is a raw pointer type subject to keyword mut.

4.7.2:3 An immutable raw pointer type is a raw pointer type subject to keyword const.

4.7.2:4 Comparing two values of raw pointer types compares the addresses of the values.

4.7.2:5 Comparing a value of a raw pointer type to a value of a dynamically sized type compares the data being pointed to.

Examples

*const i128
*mut bool

4.7.3. Reference Types

Syntax

ReferenceTypeSpecification ::=
    & LifetimeIndication? mut? TypeSpecificationWithoutBounds

Legality Rules

4.7.3:1 A reference type is an indirection type with ownership.

4.7.3:2 A shared reference type is a reference type not subject to keyword mut.

4.7.3:3 A shared reference type prevents the direct mutation of a referenced value.

4.7.3:4 A shared reference type implements the core::marker::Copy trait. Copying a shared reference performs a shallow copy.

4.7.3:5 Releasing a shared reference has no effect on the value it refers to.

4.7.3:6 A mutable reference type is a reference type subject to keyword mut.

4.7.3:7 A mutable reference type allows the direct mutation of a referenced value.

4.7.3:8 A mutable reference type does not implement the copy::marker::Copy trait.

Undefined Behavior

4.7.3:9 It is validity invariant for a value of a reference type to be not null.

Examples

&i16
&'a mut f32

4.8. Trait Types

4.8.1. Impl Trait Types

Syntax

ImplTraitTypeSpecification ::=
    impl TypeBoundList

ImplTraitTypeSpecificationOneBound ::=
    impl TraitBound

Legality Rules

4.8.1:1 An impl trait type is a type that implements a trait, where the type is known at compile time.

4.8.1:2 An impl trait type shall appear only within a function parameter or the return type of a function.

Examples

fn anonymous_type_parameter
    (arg: impl Copy + Send + Sync) { ... }

fn anonymous_return_type () -> impl MyTrait { ... }

4.8.2. Trait Object Types

Syntax

TraitObjectTypeSpecification ::=
    dyn TypeBoundList

TraitObjectTypeSpecificationOneBound ::=
    dyn TraitBound

Legality Rules

4.8.2:1 A trait object type is a type that implements a trait, where the type is not known at compile time.

4.8.2:2 The first trait bound of a trait object type shall denote an object safe trait. Any subsequent trait bounds shall denote auto traits.

4.8.2:3 A trait object type shall not contain opt-out trait bounds.

4.8.2:4 A trait object type shall contain at most one lifetime bound.

4.8.2:5 A trait object type is a dynamically sized type. A trait object type permits late binding of methods. A method invoked via a trait object type involves dynamic dispatching.

Examples

dyn MyTrait
dyn MyTrait + Send
dyn MyTrait + 'static + Copy

4.9. Other Types

4.9.1. Inferred Types

Syntax

InferredType ::=
    _

Legality Rules

4.9.1:1 An inferred type is a placeholder for a type deduced by type inference.

4.9.1:2 An inferred type shall not appear in the following positions:

4.9.1:7 An inferred type forces a tool to deduce a type, if possible.

Examples

let values: Vec<_> = (0 .. 10).collect();

4.9.2. Never Type

Syntax

NeverType ::=
    !

Legality Rules

4.9.2:1 The never type is a type that represents the result of a computation that never completes.

4.9.2:2 The never type has no values.

Undefined Behavior

4.9.2:3 It is validity variant to not have a value of the never type.

Examples

let never_completes: ! = panic!();

4.9.3. Parenthesized Types

Syntax

ParenthesizedTypeSpecification ::=
    ( TypeSpecification )

Legality Rules

4.9.3:1 A parenthesized type is a type that disambiguates the interpretation of lexical elements

Examples

&'a (dyn MyTrait + Send)

4.10. Type Aliases

Syntax

TypeAliasDeclaration ::=
    type Name GenericParameterList? (: TypeBoundList)? WhereClause?
      = InitializationType WhereClause? ;

InitializationType ::=
    TypeSpecification

Legality Rules

4.10:1 A type alias is an item that defines a name for a type.

4.10:2 A type alias shall not have a TypeBoundList unless it is an associated item.

4.10:3 The last where clause is rejected, but may still be consumed by macros.

Examples

type Point = (f64, f64);

4.11. Representation

4.11.1. Type Layout

Legality Rules

4.11.1:1 All values have an alignment and a size.

4.11.1:2 The alignment of a value specifies which addresses are valid for storing the value. Alignment is measured in bytes, is at least one, and always a power of two. A value of alignment N is stored at an address that is a multiple of N.

4.11.1:3 The size of a value is the offset in bytes between successive elements in array type [T, N] where T is the type of the value, including any padding for alignment. Size is a multiple of the alignment.

4.11.1:4 The size of scalar types is as follows:

4.11.1:5

Type

Size

4.11.1:6

bool

1

4.11.1:7

u8, i8

1

4.11.1:8

u16, i16

2

4.11.1:9

u32, i32

4

4.11.1:10

u64, i64

8

4.11.1:11

u128, i128

16

4.11.1:12

f32

4

4.11.1:13

f64

8

4.11.1:14

char

4

4.11.1:15 Types usize and isize have size big enough to contain every address on the target platform.

4.11.1:16 For type str, the layout is that of slice type [u8].

4.11.1:17 For array type [T; N] where T is the element type and N is size operand, the alignment is that of T, and the size is calculated as core::mem::size_of::<T>() * N.

4.11.1:18 For a slice type, the layout is that of the array type it slices.

4.11.1:19 For a tuple type, the layout is tool-defined. For a unit tuple, the size is zero and the alignment is one.

4.11.1:20 For a closure type, the layout is tool-defined.

4.11.1:21 For a thin pointer, the size and alignment are those of type usize.

4.11.1:22 For a fat pointer, the size and alignment are tool-defined, but are at least those of a thin pointer.

4.11.1:23 For a trait object type, the layout is the same as the value being coerced into the trait object type at runtime.

4.11.1:24 For a struct type, the memory layout is undefined, unless the struct type is subject to attribute repr.

4.11.1:25 For a union type, the memory layout is undefined, unless the union type is subject to attribute repr. All union fields share a common storage.

4.11.1:26 The size of a recursive type shall be finite.

4.11.2. Type Representation

Legality Rules

4.11.2:1 Type representation specifies the layout of fields of abstract data types. Type representation changes the bit padding between fields of abstract data types as well as their order, but does not change the layout of the fields themselves.

4.11.2:2 Type representation is classified into:

4.11.2:7 C representation lays out a type such that the type is interoperable with the C language.

4.11.2:8 Default representation makes no guarantees about the layout.

4.11.2:9 Primitive representation is the type representation of individual integer types. Primitive representation applies only to an enum type that is not a zero-variant enum type. It is possible to combine C representation and primitive representation.

4.11.2:10 Transparent representation applies only to an enum type with a single enum variant or a struct type where the struct type has a single field of non-zero size and any number of fields of size zero and alignment one.

4.11.2:11 Types subject to transparent representation have the same type representation as a struct type with a single field of a non-zero size.

4.11.2:12 Type representation may be specified using attribute repr. An enum type, a struct type, or a union type that is not subject to attribute repr has default representation.

4.11.2:13 Type representation may be modified using attribute repr's align and packed representation modifiers. A representation modifier shall apply only to a struct type or a union type subject to C representation or default representation.

4.11.2.1. Enum Type Representation

Legality Rules

4.11.2.1:1 Zero-variant enum types shall not be subject to C representation.

4.11.2.1:2 The size and alignment of an enum type without fields subject to C representation, default representation, or primitive representation are those of its discriminant.

4.11.2.1:3 The discriminant type of an enum type with C representation is the type of a C enum for the target platform’s C ABI.

4.11.2.1:4 The discriminant type of an enum type with default representation is tool-defined.

4.11.2.1:5 The discriminant type of an enum type with primitive representation is the primitive type specified by the primitive representation.

4.11.2.1:6 It is a static error if the discriminant type cannot hold all the discriminant values of an enum type.

4.11.2.1:7 An enum type subject to transparent representation shall have a single enum variant with

4.11.2.1:10 An enum type subject to C representation or primitive representation has the same type representation as a union type with C representation that is laid out as follows:

4.11.2.1:14 An enum type subject to transparent representation has the same type representation as the single field of non-zero size of its enum variant if one is present, otherwise the enum type has size zero and alignment one.

4.11.2.2. Struct Type Representation

Legality Rules

4.11.2.2:1 The alignment of a struct type subject to C representation is the alignment of the most-aligned field in it.

4.11.2.2:2 The size of a struct type subject to C representation is determined as follows:

  1. 4.11.2.2:3 Initialize a current offset to zero.

  2. 4.11.2.2:4 For each field of the struct type in declarative order:

    1. 4.11.2.2:5 Calculate the size and alignment of the field.

    2. 4.11.2.2:6 If the current offset is not a multiple of the field's alignment, add byte padding to the current offset until it is a multiple of the alignment. The offset of the field is the current offset.

    3. 4.11.2.2:7 Increase the current offset by the size of the field.

    4. 4.11.2.2:8 Proceed with the next field.

  3. 4.11.2.2:9 Round up the current offset to the nearest multiple of the struct type's alignment.

  4. 4.11.2.2:10 The size of the struct type is the current offset.

4.11.2.2:11 A struct type subject to transparent representation shall have:

4.11.2.2:14 A struct type subject to transparent representation has the same type representation as the single field of non-zero size if one is present, otherwise the struct type has size zero and alignment one.

4.11.2.3. Union Type Representation

Legality Rules

4.11.2.3:1 The size of a union type subject to C representation is the maximum of the sizes of all its fields, rounded up to alignment of the union type.

4.11.2.3:2 The alignment of a union type subject to C representation is the maximum of the alignments of all of its fields.

4.12. Type Model

4.12.1. Recursive Types

Legality Rules

4.12.1:1 A recursive type is a category of type that may define other types within its type specification.

4.12.1:2 A recursive type shall include at least one abstract data type in its recursion.

Examples

enum List<T> {
    Nil,
    Cons(T, Box<List<T>>)
}

4.12.2. Type Unification

Legality Rules

4.12.2:1 Type unification is a measure of compatibility between two types. A type is said to unify with another type when the domains, ranges and structures of both types are compatible.

4.12.2:2 Two types that unify are said to be unifiable types.

4.12.2:3 A scalar type is unifiable only with itself.

4.12.2:4 The never type is unifiable with any other type.

4.12.2:5 An array type is unifiable only with another array type when

4.12.2:8 A slice type is unifiable only with another slice type when the element types of both slice types are unifiable.

4.12.2:9 Type str is unifiable only with itself.

4.12.2:10 A tuple type is unifiable only with another tuple type when:

4.12.2:13 An abstract data type is unifiable only with another abstract data type when:

4.12.2:16 A closure type is unifiable only with another closure type when:

4.12.2:19 A function item type is unifiable only with another function item type when:

4.12.2:22 A function pointer type is unifiable only with another function pointer type when:

4.12.2:30 A raw pointer type is unifiable only with another raw pointer type when:

4.12.2:33 A reference type is unifiable only with another reference type when:

4.12.2:36 An anonymous return type is unifiable with another type when:

4.12.2:39 An impl trait type is unifiabe only with itself.

4.12.2:40 A trait object type is unifiable only with another trait object type when:

  • 4.12.2:41 The bounds are unifiable, and

  • 4.12.2:42 The lifetimes are unifiable.

4.12.2:43 A global type variable is unifiable with any other type.

4.12.2:44 A floating-point type variable is unifiable only with a floating-point type.

4.12.2:45 An integer type variable is unifiable only with an integer type.

4.12.2:46 A type alias is unifiable with another type when the aliased type is unifiable with the other type.

4.12.3. Type Coercion

Legality Rules

4.12.3:1 Type coercion is an implicit operation that changes the type of a value. Any implicit conversion allowed by type coercion can be made explicit using a type cast expression.

4.12.3:2 A type coercion takes place at a coercion site or within a coercion-propagating expression.

4.12.3:3 The following constructs constitute a coercion site:

4.12.3:10 The following expressions constitute a coercion-propagating expression:

4.12.3:15 Type coercion from a source type to a target type is allowed to occur when:

  • 4.12.3:16 The source type is a subtype of the target type.

  • 4.12.3:17 The source type T coerces to intermediate type W, and intermediate type W coerces to target type U.

  • 4.12.3:18 The source type is &T and the target type is *const T.

  • 4.12.3:19 The source type is &T and the target type is &U, where T implements the core::ops::Deref<Target = U> trait.

  • 4.12.3:20 The source type is &mut T and the target type is &T.

  • 4.12.3:21 The source type is &mut T and the target type is *mut T.

  • 4.12.3:22 The source type is &mut T and the target type is &U, where T implements the core::ops::Deref<Target = U> trait.

  • 4.12.3:23 The source type is &mut T and the target type is &mut U, where T implements the core::ops::DerefMut<Target = U> trait.

  • 4.12.3:24 The source type is *mut T and the target type is *const T.

  • 4.12.3:25 The source type is type_constructor(T) and the target type is type_constructor(U), where type_constructor is one of &W, &mut W, *const W, or *mut W, and U can be obtained from T using unsized coercion.

  • 4.12.3:26 The source type is a function item type and the target type is a function pointer type.

  • 4.12.3:27 The source type is a non-capturing closure type and the target type is a function pointer type.

  • 4.12.3:28 The source type is the never type and the target type is any type.

4.12.3:29 An unsized coercion is a type coercion that converts a sized type into an unsized type. Unsized coercion from a source type to a target type is allowed to occur when:

  • 4.12.3:30 The source type is array type [T; N] and the target type is slice type [T].

  • 4.12.3:31 The source type is T and the target type is dyn U, where T implements U + core::marker::Sized, and U is object safe.

  • 4.12.3:32 The source type is

S<..., T, ...> {
    ...
    last_field: X
}

4.12.3:33 where

  • 4.12.3:34 S is a struct type,

  • 4.12.3:35 T implements core::marker::Unsize<U>,

  • 4.12.3:36 last_field is a struct field of S,

  • 4.12.3:37 The type of last_field involves T and if the type of last_field is W<T>, then W<T> implements core::marker::Unsize<W<U>>,

  • 4.12.3:38 T is not part of any other struct field of S.

4.12.3:39 and the target type is S<..., U, ...>.

4.12.3:40 Least upper bound coercion is a multi-type coercion that is used in the following scenarios:

4.12.3:47 Least upper bound coercion considers a set of source types T1, T2, ..., TN and target type U. The target type is obtained as follows:

  1. 4.12.3:48 Initialize target type U to source type T1.

  2. 4.12.3:49 For each current source type TC in the inclusive range T1 to TN

    1. 4.12.3:50 If TC can be coerced to U, then continue with the next source type.

    2. 4.12.3:51 Otherwise if U can be coerced to TC, make TC the target type U.

    3. 4.12.3:52 Otherwise compute the mutual supertype of TC and U, make the mutual supertype be target type U. It is a static error if the mutual supertype of TC and U cannot be computed.

    4. 4.12.3:53 Continue with the next source type.

4.12.4. Structural Equality

Legality Rules

4.12.4:1 A type is structurally equal when its values can be compared for equality by structure.

4.12.4:2 The following types are structurally equal:

4.12.5. Interior Mutability

Legality Rules

4.12.5:1 Interior mutability is a property of types whose values can be modified through immutable references.

4.12.5:2 A type is subject to interior mutability when it contains a core::cell::UnsafeCell.

4.12.6. Type Inference

Legality Rules

4.12.6:1 Constant declarations, let statements, and static declarations impose an expected type on their respective initialization expressions. Type inference is the process of deducing the expected type of an arbitrary value.

4.12.6:2 A type variable is a placeholder for a type. A global type variable is a type variable that can refer to any type.

4.12.6:3 The expected type of the constant initializer of a constant is the type specified by its type ascription.

4.12.6:4 The expected type of the initialization expression of a let statement is determined as follows:

  1. 4.12.6:5 If the let statement appears with a type ascription, then the expected type is the type specified by its type ascription.

  2. 4.12.6:6 Otherwise the expected type is a global type variable.

4.12.6:7 The expected type of the static initializer of a static is the type specified by its type ascription.

4.12.6:8 Arithmetic expressions, await expressions, bit expressions, block expressions, borrow expressions, dereference expressions, call expressions, else expressions, error propagation expressions, if expressions, if let expressions, loop expressions, match expressions, negation expressions, and parenthesized expressions are type imposing expressions.

4.12.6:9 A type imposing expression imposes its expected type onto a nested construct, as follows:

4.12.6:34 Array expressions, index expressions, assignment expressions, closure expressions, comparison expressions, compound assignment expressions, field access expressions, lazy boolean expressions, method call expressions, range expressions, struct expressions, tuple expressions, and type cast expressions are type resolving expressions.

4.12.6:35 A type resolving expression provides a resolving type, which is the type of the expression itself.

4.12.6:36 A floating-point type variable is a type variable that can refer only to floating-point types.

4.12.6:37 The resolving type of a float literal is determined as follows:

  1. 4.12.6:38 If the float literal has a float suffix, then the resolving type is the type specified by its float suffix.

  2. 4.12.6:39 Otherwise the resolving type is a floating-point type variable.

4.12.6:40 An integer type variable is a type variable that can refer only to integer types.

4.12.6:41 The resolving type of an integer literal is determined as follows:

  1. 4.12.6:42 If the integer literal has an integer suffix, then the resolving type is the type specified by its integer suffix.

  2. 4.12.6:43 Otherwise the resolving type is an integer type variable.

4.12.6:44 Constant arguments, constant declarations, functions, and static declarations are referred to as type inference roots.

4.12.6:45 Type inference for a single type inference root proceeds as follows:

  1. 4.12.6:46 Determine unique expected type ET for the type inference root.

  2. 4.12.6:47 Resolve the initialization expression of the type inference root against ET as follows:

    1. 4.12.6:48 If the expression is a type imposing expression, then

      1. 4.12.6:49 Make ET the type of the expression.

      2. 4.12.6:50 Impose ET on any nested construct depending on the nature of the expression, recursively.

    2. 4.12.6:51 If the expression is a type resolving expression, then

      1. 4.12.6:52 Determine resolving type RT the expression.

      2. 4.12.6:53 Resolve ET against RT.

  3. 4.12.6:54 If there are expressions whose type T is a floating-point type variable, replace T with type f64.

  4. 4.12.6:55 If there are expressions whose type T is an integer type variable, replace T with type i32.

  5. 4.12.6:56 If there are expressions whose type is a global type variable, then this is a static error.

4.12.6:57 Resolving expected type ET against resolving type RT for an expression proceeds as follows:

  1. 4.12.6:58 If both ET and RT denote a concrete type, then ET and RT shall be unifiable.

  2. 4.12.6:59 If ET denotes a global type variable and RT denotes a concrete type, then ET is replaced with RT, effectively changing the type of all expressions that previously held ET.

  3. 4.12.6:60 If ET denotes a floating-point type variable and RT denotes a floating point type, then ET is replaced with RT, effectively changing the type of all expressions that previously held ET.

  4. 4.12.6:61 If ET denotes an integer type variable and RT denotes an integer type, then ET is replaced with RT, effectively changing the type of all expressions that previously held ET.

  5. 4.12.6:62 Otherwise this is a static error.

4.13. Traits

Syntax

TraitDeclaration ::=
    unsafe? trait Name GenericParameterList? (: SupertraitList?)? WhereClause? {
      InnerAttributeOrDoc*
      AssociatedItem*
    }

SupertraitList ::=
    TypeBoundList

Legality Rules

4.13:1 A trait is an item that describes an interface a type can implement.

4.13:2 A local trait is a trait that is defined in the current crate.

B.345:1 A subtrait is a trait with a supertrait.

4.13:4 A supertrait is a transitive trait that a type must additionally implement.

4.13:5 A subtrait shall not be its own supertrait.

4.13:6 A trait of the form

trait T: Bound {}

4.13:7 is equivalent to a where clause of the following form:

trait T where Self: Bound {}

Examples

trait Sequence<T> {
    fn length(&self) -> u32;
    fn element_at(&self, position: u32) -> T;
}

4.13:8 Shape is a supertrait of Circle.

trait Shape {
    fn area(&self) -> f64;
}

4.13:9 Circle is a subtrait of Shape.

trait Circle: Shape {
    fn radius(&self) -> f64;
}

4.13.1. Object Safety

Legality Rules

4.13.1:1 A trait is object safe when:

4.13.1:6 An associated function is object safe when it is either an object safe dispatchable function or an object safe non-dispatchable function.

4.13.1:7 A dispatchable function is object safe when:

4.13.1:12 A non-dispatchable function is object safe when it specifies a core::marker::Sized trait bound for Self.

4.14. Trait and Lifetime Bounds

Syntax

TypeBoundList ::=
    TypeBound (+ TypeBound)* +?

TypeBound ::=
    LifetimeIndication
  | ParenthesizedTraitBound
  | TraitBound

LifetimeIndication ::=
    Lifetime
  | '_
  | 'static

LifetimeIndicationList ::=
    LifetimeIndication (+ LifetimeIndication)* +?

ParenthesizedTraitBound ::=
    ( TraitBound )

TraitBound ::=
    ?? ForGenericParameterList? TypePath

ForGenericParameterList ::=
    for GenericParameterList

Legality Rules

4.14:1 A bound imposes a constraint on a generic parameter by limiting the set of possible generic substitutions.

4.14:2 A bound does not impose a constraint on a generic parameter of a type alias unless it is an associated item.

4.14:3 A lifetime bound is a bound that imposes a constraint on the lifetimes of generic parameters.

4.14:4 A trait bound is a bound that imposes a constraint on the traits of generic parameters.

4.14:5 A ForGenericParameterList shall not specify ConstantParameters or TypeParameters.

4.14:6 A higher-ranked trait bound is a bound that specifies an infinite list of bounds for all possible lifetimes specified by the ForGenericParameterList.

4.14:7 Bound 'a: 'b is read as 'a outlives 'b, or in other words, 'a lasts as long as 'b.

4.14:8 Bound T: 'a indicates that all lifetime parameters of T outlive 'a.

Examples

fn draw<T: Shape>(shape: T) { ... }

4.14.1. Lifetimes

Syntax

Lifetime ::=
    ' NonKeywordIdentifier

AttributedLifetime ::=
    OuterAttributeOrDoc* Lifetime

AttributedLifetimeList ::=
    AttributedLifetime (, AttributedLifetime)* ,?

Legality Rules

4.14.1:1 A lifetime specifies the expected longevity of a value.

4.14.1:2 A lifetime bound shall apply to types and other lifetimes.

Examples

&'a i32
&'static Shape

4.14.1:3 See Paragraph 4.12. for the declaration of Shape.

4.14.2. Subtyping and Variance

Legality Rules

4.14.2:1 Subtyping is a property of types, allowing one type to be used where another type is expected.

4.14.2:2 Variance is a property of lifetime parameters and type parameters that describes the circumstances under which a generic type is a subtype of an instantiation of itself with different generic arguments.

4.14.2:3 A type is its own subtype.

4.14.2:4 F<T> is said to be

  • 4.14.2:5 Covariant over T when T being a subtype of U implies that F<T> is a subtype of F<U>, or

  • 4.14.2:6 Contravariant over T when T being a subtype of U implies that F<U> is a subtype of F<T>, or

  • 4.14.2:7 Invariant over T.

4.14.2:8 Variance is determined as follows:

4.14.2:9

Type

Variance in ‘a

Variance in T

4.14.2:10

&'a T

covariant

covariant

4.14.2:11

&'a mut T

covariant

invariant

4.14.2:12

*const T

covariant

4.14.2:13

*mut T

invariant

4.14.2:14

[T]

covariant

4.14.2:15

[T; N]

covariant

4.14.2:16

fn() -> T

covariant

4.14.2:17

fn(T) -> ()

contravariant

4.14.2:18

fn(T) -> T

invariant

4.14.2:19

core::call::UnsafeCell<T>

invariant

4.14.2:20

core::marker::PhantomData<T>

covariant

4.14.2:21

dyn Trait<T> + 'a

covariant

invariant

4.14.2:22 A trait is invariant in all inputs, including the Self parameter.

4.14.2:23 Lifetime parameters and type parameters are subject to variance.

4.14.2:24 The variance of a generic parameter of an abstract data type or an tuple type is determined as follows:

  1. 4.14.2:25 For each generic parameter G

    1. 4.14.2:26 Initialize variance V of the generic parameter to any.

    2. 4.14.2:27 For each field of the abstract data type or the tuple type

      1. 4.14.2:28 If field type T uses G, then

        1. 4.14.2:29 If V is any, set V to the variance of T over G.

        2. 4.14.2:30 Otherwise if V and the variance of T over G differ, set V to invariant.

    3. 4.14.2:31 It is a static error if variance V is any.

4.14.3. Lifetime Elision

Legality Rules

4.14.3:1 Lifetime elision is a set of relaxations on the use of lifetimes.

4.14.3.1. Function Lifetime Elision

Legality Rules

4.14.3.1:1 Function lifetime elision is a form of lifetime elision that applies to functions, function pointer type parameters and paths resolving to one of the core::ops::Fn, core::ops::FnMut, and core::ops::FnOnce traits.

4.14.3.1:2 An input lifetime is one of the following lifetimes:

4.14.3.1:7 An output lifetime is one of the following lifetimes:

4.14.3.1:11 Lifetime elision proceeds as follows:

  1. 4.14.3.1:12 Each elided input lifetime is a distinct lifetime parameter in its related construct.

  2. 4.14.3.1:13 If a construct has exactly one input lifetime, then that lifetime is assigned to all elided output lifetimes.

  3. 4.14.3.1:14 If a function has a receiver of the form &self, &mut self, or self: T where T is a type with a lifetime, then the lifetime of the receiver is assigned to all elided output lifetimes.

  4. 4.14.3.1:15 Otherwise this is a static error.

Examples

4.14.3.1:16 Given function f of the form

fn f <'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command;

4.14.3.1:17 its lifetime elided` form is

fn f <T: ToCStr>(&mut self, args: &[T]) -> &mut Command;

4.14.3.2. Static Lifetime Elision

Legality Rules

4.14.3.2:1 Static lifetime elision is a form of lifetime elision that applies to constants and statics.

4.14.3.2:2 An elided lifetime of a reference type or path in the type specification of a constant or static is inferred to be the 'static' lifetime.

4.14.3.2:3 If function lifetime elision is applicable for a lifetime, static lifetime elision is not applied for that lifetime.

4.14.3.2:4 The lifetime of an associated implementation constant shall not be elided.

4.14.3.2:5 The lifetime of an associated trait constant shall not be elided.

Examples

4.14.3.2:6 Given static S of the form

static S: &[&usize] = &[];

4.14.3.2:7 its lifetime elided form is

static S: &'static [&'static usize] = &[];

4.14.3.3. Trait Object Lifetime Elision

Legality Rules

4.14.3.3:1 Trait object lifetime elision is a form of lifetime elision that applies to trait object types.

4.14.3.3:2 An elided lifetime of a trait object type is inferred as follows:

Examples

4.14.3.3:10 Given type alias T of the form

type T<'a> = &'a dyn Trait;

4.14.3.3:11 its lifetime elided form is

type T<'a> = &'a (dyn Trait + 'a);