Generics

Generic Constraints

A generic constraint (also called a trait bound) is a compile-time requirement attached to a generic type parameter (T: Trait) specifying which traits a concrete type must implement to be substituted for T.

Why Unconstrained Generics Are Restricted

Without constraints, the compiler assumes nothing about T beyond the default implicit Sized bound. A function with an unconstrained <T> cannot add, compare, clone, or print values of type T because not every Rust type supports those operations.

Unconstrained <T>               Constrained <T: Clone + PartialOrd + Display>
+-------------------------+     +---------------------------------------------+
| Allowed:                |     | Allowed:                                    |
| - move value            |     | - everything on unconstrained T             |
| - borrow (&T, &mut T)   |     | - value.clone()                             |
| - drop value            |     | - a < b, a <= b, a > b, a >= b              |
| - std::mem::size_of<T>  |     | - format!("{value}")                        |
+-------------------------+     +---------------------------------------------+

Inline Bound Syntax vs. where Clauses

Rust provides two equivalent ways to write generic constraints:

  1. Inline bounds inside the <...> parameter list (<T: Clone + Debug>), suited for short signatures with one or two bounds.
  2. where clauses placed just before the opening { of the function or impl block, suited for multiple parameters, associated types, or complex multi-trait constraints.
use std::fmt::{Debug, Display};
use std::ops::Add;

// 1. Inline constraint syntax:
fn double_value<T: Copy + Add<Output = T>>(x: T) -> T {
    x + x
}

// 2. Equivalent `where` clause syntax on a multi-parameter signature:
fn log_addition<A, B, Out>(left: A, right: B) -> Out
where
    A: Copy + Display + Add<B, Output = Out>,
    B: Copy + Display,
    Out: Debug,
{
    let result = left + right;
    println!("{left} + {right} = {result:?}");
    result
}

fn main() {
    println!("double_value(21) = {}", double_value(21));
    let sum = log_addition(12.5_f64, 7.5_f64);
    println!("returned: {sum}");
}
double_value(21) = 42
12.5 + 7.5 = 20.0
returned: 20

Combining Multiple Constraints with +

When a type parameter must satisfy more than one trait simultaneously, join the traits with +. A concrete type argument is accepted only if it implements every trait in the + list:

#[derive(Debug, Clone)]
struct Config {
    threads: usize,
}

fn duplicate_and_inspect<T: Clone + std::fmt::Debug>(item: &T) -> (T, T) {
    let first = item.clone();
    let second = item.clone();
    println!("Duplicated: {first:?}");
    (first, second)
}

fn main() {
    let cfg = Config { threads: 8 };
    let (a, b) = duplicate_and_inspect(&cfg);
    println!("a.threads = {}, b.threads = {}", a.threads, b.threads);
}
Duplicated: Config { threads: 8 }
a.threads = 8, b.threads = 8

Relaxing the Implicit Sized Constraint with ?Sized

Every generic type parameter T in Rust carries an implicit T: Sized constraint by default, meaning T's byte size must be known at compile time. Dynamically sized types (DSTs) such as str and [u8] do not implement Sized.

To allow a generic function or struct to accept dynamically sized types behind a reference or pointer, write T: ?Sized ("T may or may not be Sized"):

use std::fmt::Debug;

fn inspect_ref<T: Debug + ?Sized>(value: &T) {
    println!("Inspected: {value:?}");
}

fn main() {
    // Works with Sized types (&i32, &Vec<i32>):
    inspect_ref(&42);

    // Also works with unsized slices (&str, &[i32]) because of `?Sized`:
    inspect_ref("unsized string slice");
    inspect_ref(&[1, 2, 3][..]);
}
Inspected: 42
Inspected: "unsized string slice"
Inspected: [1, 2, 3]
Bound Syntax Meaning
<T> T must be Sized (implicit default)
<T: Display> T must implement Display and be Sized
<T: Display + Clone> T must implement both Display and Clone (and Sized)
<T: ?Sized> T may be either Sized or dynamically sized (str, [U], dyn Trait)
<T: 'static> T cannot contain any non-'static borrowed references