Every value has a type, fixed at compile time. Rust is statically typed with local
type inference, and it has no null value for ordinary types (Reference:
Type system). This page covers the built-in types. User-defined types are on the
Structs, enums and unions page.
Type expressions
Type:
TypeNoBounds | ImplTraitType | TraitObjectType
TypeNoBounds:
ParenthesizedType | ImplTraitTypeOneBound | TraitObjectTypeOneBound
| TypePath | TupleType | NeverType | RawPointerType | ReferenceType
| ArrayType | SliceType | InferredType | QualifiedPathInType
| BareFunctionType | MacroInvocation
| Kind | Types |
|---|---|
| Primitive | bool, integers, floats, char, str, ! |
| Sequence | Tuple, array, slice |
| User-defined | struct, enum, union |
| Function | Function items, closures |
| Pointer | Reference, raw pointer, function pointer |
| Trait | Trait object (dyn), impl Trait |
Parentheses are sometimes required to tell where a bound ends:
&'a (dyn Any + Send). A recursive type must pass through a nominal
type (a struct, enum or union) and have finite size, so a recursive field needs a
pointer such as Box.
Numeric types
| Signed | Unsigned | Bytes | Range of signed | Range of unsigned |
|---|---|---|---|---|
i8 | u8 | 1 | −128 to 127 | 0 to 255 |
i16 | u16 | 2 | −32768 to 32767 | 0 to 65535 |
i32 | u32 | 4 | −231 to 231−1 | 0 to 232−1 |
i64 | u64 | 8 | −263 to 263−1 | 0 to 264−1 |
i128 | u128 | 16 | −2127 to 2127−1 | 0 to 2128−1 |
isize | usize | Pointer size | At least 16 bits; the width of a memory address | |
f32 and f64 are IEEE 754 binary32 and binary64. Signed
integers are two’s complement. Each usize is the same size as a pointer
on the target; the size of any one object cannot exceed isize::MAX bytes.
A literal’s type comes from its
suffix or from inference.
Boolean, character and string slice
boolis one byte,0for false and1for true. Any other value is undefined behaviour.charis four bytes and holds one Unicode scalar value:U+0000toU+D7FForU+E000toU+10FFFF. A surrogate value is undefined behaviour.stris a string slice. It is a sequence of bytes with the layout of[u8], and standard library code assumes it holds valid UTF-8. It is unsized, and is normally used behind a reference, as&str.
The never type
! is the type with no values. It marks a computation that never
finishes, and it coerces to any other type. Today it can appear only in function
return types (Reference: Never type). The fall-back type of an expression of type
! whose type is otherwise unconstrained changed in
edition 2024.
fn fail() -> ! {
panic!("This call never returns.");
}
let n: u32 = if ok { 1 } else { fail() }; // the else arm has type !, which coerces to u32
Tuples, arrays and slices
| Type | Form | Size | Notes |
|---|---|---|---|
| Tuple | (A, B, C) | Sized if every element is | Layout is unspecified. A one-element tuple needs a comma: (A,). Elements are accessed with t.0, t.1 |
| Unit | () | 0 bytes, alignment 1 | The empty tuple. The value of a block with no tail expression |
| Array | [T; N] | size_of::<T>() * N | N is a constant of type usize. Elements are stored one after another |
| Slice | [T] | Unsized | A view into consecutive elements. Normally behind a pointer: &[T], &mut [T], Box<[T]> |
Indexing an array or slice outside its bounds panics at run time. A mutable reference to a slice lets its elements be changed but not its length.
Pointer types
| Type | Meaning |
|---|---|
&T, &'a T | Shared reference. Many may exist; the target cannot normally be changed through it (see Ownership and borrowing) |
&mut T, &'a mut T | Exclusive reference. Only one active at a time; the target can be changed through it |
*const T, *mut T | Raw pointers. No lifetime and no aliasing guarantee; dereferencing requires unsafe |
fn(A) -> B | Function pointer. May also be written with unsafe, extern "ABI" or a for<'a> list |
Box<T>, Rc<T>, Arc<T> | Smart pointers from the standard library; they are ordinary structs, but Box has special treatment |
BareFunctionType:
ForLifetimes? FunctionTypeQualifiers fn ( FunctionParametersMaybeNamedVariadic? ) ( -> TypeNoBounds )?
FunctionTypeQualifiers:
unsafe? ( extern Abi? )?
fn add(x: i32, y: i32) -> i32 { x + y }
type Binop = fn(i32, i32) -> i32;
let bo: Binop = add; // a function item coerces to a function pointer
let z = bo(5, 7);
A function pointer is never null. Null or nullable C pointers are modelled with
Option<fn(…)>. Pointers to sized types have the same size as
usize. Pointers to unsized types are wide: they hold a second
word, the length of a slice or the vtable of a trait object.
Function items and closures
Each function, and each tuple struct or enum variant constructor, has its own zero-sized function item type, which can be called and coerces to a function pointer of the matching signature. Each closure has a unique anonymous type, which is roughly a struct holding its captured values. Neither type can be written in source.
Closures implement FnOnce; those that do not move out of captures also
implement FnMut; those that also do not mutate captures implement
Fn. A closure that captures nothing coerces to a function pointer.
Trait objects
TraitObjectType: Bounds | dyn Bounds?
TraitObjectTypeOneBound: TraitBound | dyn TraitBound?
A trait object is an opaque value of some other type that implements a set of traits: one dyn compatible base trait plus any number of auto traits (Reference: Trait object types).
- There must be at least one trait, no more than one non-auto trait and no more
than one lifetime.
?Sizedanduse<…>bounds are not allowed. - A trait object is dynamically sized, so it must be behind a pointer such as
&dyn TraitorBox<dyn Trait>. The pointer holds a data pointer and a pointer to a vtable, and method calls go through the vtable. - Bare trait objects without
dynare accepted only in editions 2015 and 2018. From edition 2021dynis required. - The default lifetime bound of a trait object is described under default object lifetimes.
trait Printable { fn stringify(&self) -> String; }
impl Printable for i32 {
fn stringify(&self) -> String { self.to_string() }
}
fn print(a: Box<dyn Printable>) {
println!("{}", a.stringify()); // dynamic dispatch through the vtable
}
impl Trait
ImplTraitType: impl Bounds?
ImplTraitTypeOneBound: impl TraitBound?
impl Trait may appear only as a parameter type or return type of a
non-extern function, not as the type of a let binding or a
field, and not in a type alias (Reference: Impl trait type).
| Position | Meaning |
|---|---|
| Argument | An anonymous type parameter: the caller chooses the type. Callers cannot name the parameter with a turbofish |
| Return | An opaque type the function chooses and hides from callers. Every return path must have the same type |
| Return, in a trait | Each occurrence becomes an anonymous associated type, and the implementation chooses it |
fn with_generic<T: Trait>(arg: T) {} // the caller picks T
fn with_impl(arg: impl Trait) {} // nearly the same; the type has no name
fn adder(n: i32) -> impl Fn(i32) -> i32 { // the function picks the type
move |x| x + n
}
A return-position impl Trait captures generic parameters that its
hidden type may use. In edition 2024, all in-scope type, const and lifetime parameters
are captured automatically. Before edition 2024, free functions and inherent methods
do not capture a lifetime parameter that does not appear in the bounds.
A use<…> bound gives the exact list: impl Sized + use<'a, T>
captures only 'a and T. All in-scope type and const
parameters must be listed, every lifetime named in the other bounds must be listed,
and lifetimes come first. The bound cannot be used on a function with an
argument-position impl Trait, whose parameters cannot be named.
The inferred type
_ asks the compiler to infer a type, as in let v: Vec<_> = iter.collect();.
It cannot be used in item signatures.
Coercions
A coercion is an implicit type change at specific sites: let statements
with an explicit type, static and const items, function call
arguments, struct field initialisers, function results and assigned values. If an array,
tuple, parenthesised expression or block is at such a site, its relevant sub-expressions
are too (Reference: Type coercions).
| From | To |
|---|---|
T | U, if T is a subtype of U |
&mut T | &T |
*mut T | *const T |
&T, &mut T | *const T, and *mut T from &mut T |
&T, &mut T | &U, if T: Deref<Target = U> |
&mut T | &mut U, if T: DerefMut<Target = U> |
| Function item type, non-capturing closure | Function pointer |
! | Any T |
| A sized pointer, by an unsized coercion | An unsized pointer, for &, &mut, *const, *mut and Box |
The unsized coercions are [T; n] to [T], T to
dyn U when T: U + Sized and U is dyn compatible,
and dyn T to dyn U when U is a supertrait of
T. Explicit conversions use the as
cast operator.
Layout
A type’s size, alignment and field offsets are its layout. Only the rules below are guaranteed; other details can change between compilations (Reference: Type layout). Size is always a multiple of alignment.
| Type | Size | Alignment |
|---|---|---|
bool, u8, i8 | 1 | 1 |
u16, i16 | 2 | Usually 2 |
u32, i32, f32 | 4 | Usually 4 |
char | 4 | Usually 4 |
u64, i64, f64 | 8 | Usually 8, but often 4 on 32-bit platforms |
u128, i128 | 16 | Platform-dependent, often 4 or 8 |
usize, isize | Pointer size | Same as its size on most targets |
| Pointer or reference to a sized type | Same as usize | Same as usize |
A pointer to an unsized type is currently twice the size of usize; this
is not guaranteed. () and types with only zero-sized fields are
zero-sized.
Representations
The #[repr] attribute controls the layout of a struct, enum or union.
| Form | Layout |
|---|---|
(none) or Rust | Unspecified field order. Each field’s offset is divisible by its alignment, and fields do not overlap (except zero-sized fields) |
C | Fields in declaration order, each at the next offset that fits its alignment; the size is rounded up to the alignment. Unions have every field at offset 0. Field-less enums have the size of the target C ABI’s default enum |
u8, i32 and other primitives | Enums only. A field-less enum has that size. An enum with fields is laid out as a repr(C) union of structs, each starting with the tag |
C, u8 | Keeps the repr(C) structure but uses the given primitive for the tag |
transparent | A struct or single-variant enum with one non-zero-sized field has the layout and ABI of that field. It cannot be combined with any other representation |
align(N) | Raises the alignment; N must be a power of two. It cannot be combined with packed |
packed, packed(N) | Lowers the alignment and removes padding. A packed type cannot contain an align type |
Taking a reference to a field of a packed struct is rejected when the field might
be under-aligned. Use &raw const to get a raw pointer and read it
with read_unaligned. See
Unsafe Rust.
#[repr(C)]
struct Demo { first: u8, second: u32, third: u64 } // offsets 0, 4, 8; size 16; alignment 8
#[repr(u8)]
enum Colour { Red = 1, Green = 2, Blue = 4 } // one byte, with discriminant values given
#[repr(transparent)]
struct Meters(f64); // same layout and ABI as f64
#[repr]