Functions, type aliases, structs, enums, unions, traits and implementations can be
parameterised by lifetimes, types and constants. Parameters go in angle brackets after
the item’s name, or right after impl for an implementation (Reference:
Generic parameters, Trait and lifetime bounds). The compiler generates a separate
copy of generic code for each set of concrete type arguments it is used with.
Parameters
GenericParams:
< ( GenericParam ( , GenericParam )* ,? )? >
GenericParam:
OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )
LifetimeParam: Lifetime ( : LifetimeBounds? )?
TypeParam: IDENTIFIER ( : Bounds? )? ( = Type )?
ConstParam: const IDENTIFIER : Type ( = ( BlockExpression | IDENTIFIER | -? LiteralExpression ) )?
fn pick<'a, T>(x: &'a T, _y: &T) -> &'a T { x }
struct Ref<'a, T> where T: 'a { r: &'a T }
struct Array<T, const N: usize>([T; N]);
struct EitherOrderWorks<const N: bool, U>(U);
enum Either<L, R> { Left(L), Right(R) }
trait Parser<Output> { fn parse(&self, s: &str) -> Output; }
impl<T, const N: usize> Default for Array<T, N>
where
T: Default + Copy,
{
fn default() -> Self { Array([T::default(); N]) }
}
- Lifetime parameters come first; type and const parameters may be mixed after them. The same name may not be used twice in a list.
'_and'staticare not valid lifetime parameter names.- Generic parameters are in scope only within the item they are declared on. Items inside a function body do not see the function’s parameters.
- A type parameter may have a default, used when the argument is left out:
struct Foo<T = i32>, as in the operator traitAdd<Rhs = Self>. Parameters with a default must come after those without one. Functions and implementations cannot have type parameter defaults. - Lifetime and type parameters may carry attributes. There are no built-in ones for this position, but derive macros may give them a meaning.
Lifetime parameters
See Ownership and
borrowing. A lifetime parameter may have outlives bounds of its own:
<'a, 'b: 'a>.
Unused parameters
A type or lifetime parameter of a struct or enum must be used in a field. Use
std::marker::PhantomData<T> when a parameter has no field to live
in. Const parameters may go unused in structs and enums. Every type and const
parameter on an impl must
constrain it.
Generic arguments
Types and functions take their generic arguments in angle brackets. In an
expression the path needs :: before the bracket, known as the
turbofish, so that the < is not read as a comparison.
let v = Vec::<i32>::new();
let n = "42".parse::<u32>().unwrap();
let c: Vec<_> = (1..5).collect(); // _ leaves the argument to inference
let s = std::mem::size_of::<u64>();
Where clauses
WhereClause: where ( WhereClauseItem , )* WhereClauseItem?
WhereClauseItem: Lifetime : LifetimeBounds?
| ForLifetimes? Type : Bounds?
A where clause adds bounds, not only on type parameters but also on any
type. It comes after the signature. fn f<A: Copy>() and
fn f<A>() where A: Copy mean the same.
struct A<T>
where
T: Iterator, // could be written A<T: Iterator>
T::Item: Copy, // a bound on an associated type
String: PartialEq<T>, // a bound on a concrete type
{
f: T,
}
Bounds
Bounds: Bound ( + Bound )* +?
Bound: Lifetime | TraitBound | UseBound
TraitBound: ( ? | ForLifetimes )? TypePath
| ( ( ? | ForLifetimes )? TypePath )
LifetimeBounds: ( Lifetime + )* Lifetime?
ForLifetimes: for GenericParams
| Bound | Meaning |
|---|---|
T: Clone + Debug | T implements both traits |
T: 'a | Every lifetime in T outlives 'a |
'a: 'b | 'a outlives 'b |
T: ?Sized | Relaxes the implicit Sized bound |
T: Iterator<Item = u32> | Sets an associated type |
T: Iterator<Item: Copy> | Bounds an associated type (Rust 1.79) |
impl Sized + use<'a, T> | A precise capturing bound on impl Trait |
- Type parameters (except
Selfin traits) and associated types areSizedby default.?Sizedrelaxes that, and applies only to type parameters and associated types. - Implied bounds: lifetime bounds needed for a type to be well
formed are inferred.
T: 'ais implied by a parameter of type&'a T, and lifetime bounds are inferred for struct, enum and impl definitions. Trait bounds are never inferred. - Trivially false bounds: a bound that uses neither the item’s parameters nor higher-ranked lifetimes is checked when the item is defined, and is an error if false.
Const generics
A const parameter introduces a name in the value namespace. Every use of the item must supply a value of the declared type.
Allowed types: u8, u16, u32,
u64, u128, usize, i8, i16,
i32, i64, i128, isize,
char and bool.
A const parameter may be used in any type in the item’s signature, in a const expression for an associated constant or as a parameter of an associated type, as a value in function bodies, and as a parameter of types in function bodies. It cannot be used inside an item nested in a function body.
struct Foo<const N: usize>([i32; N]);
impl<const N: usize> Foo<N> {
const CONST: usize = N * 4;
}
fn zeroes<const N: usize>() -> [u8; N] {
[0; N]
}
let _a = zeroes::<8>();
let _b: [u8; 4] = zeroes(); // inferred from the expected type
let _c = zeroes::<{ 2 + 2 }>();
- Inside a type or an array repeat expression, a const parameter must be
a standalone single-segment path, optionally in a block:
Nor{N}.[u8; {N + 1}]is an error. - Const arguments must be a const expression of the parameter’s type
or the inferred const
_. They must be wrapped in braces unless they are a single identifier or a literal (optionally negated). - Inferred const:
_is allowed where a const argument is expected, optionally in parentheses. It is not allowed in braces or in item signatures. - Ambiguity: if an argument could be a type or a const, it is read as
a type. Wrapping it in braces forces a const:
Foo<{ N }>. - Exhaustiveness is not considered by trait resolution. Even with
implementations for
Foo<true>andFoo<false>, a genericFoo<B>does not satisfy the trait bound.
Higher-ranked trait bounds
A for<'a> bound holds for every lifetime 'a. It is mainly
used with closures and function traits, where the lifetime is chosen by the code that
makes the call, not by the caller of the generic function.
// The closure must accept a reference of any lifetime, because
// `zero` lives for less time than any lifetime parameter of the function.
fn call_on_ref_zero<F>(f: F) where for<'a> F: Fn(&'a i32) {
let zero = 0;
f(&zero);
}
// The same, with for<'a> scoped to the bound.
fn call_on_ref_zero2<F>(f: F) where F: for<'a> Fn(&'a i32) { /* … */ }
// Elided lifetimes in Fn bounds are higher-ranked too:
fn call_on_ref_zero3<F: Fn(&i32)>(f: F) { /* … */ }
The for list only accepts lifetime parameters. A function pointer may
have one too: for<'a> fn(&'a str) -> &'a str.