Generics

Common Generic Errors

Working with generics in Rust surfaces a well-defined set of compile-time diagnostics when trait bounds are missing, type parameters cannot be inferred, or caller and callee perspectives on a type parameter are confused.

Reference Table of Common Compiler Diagnostics

Error Code Summary Typical Cause
E0277 Trait bound not satisfied Using an operator, format specifier, or method on T without declaring the required trait bound
E0282 Type annotations needed Calling a generic function or constructing a variant (None, Ok) without enough context to fix T
E0308 Mismatched types (caller vs. callee T) Returning a concrete type (i32) where a caller-chosen type parameter T was declared
E0392 Type parameter is never used Declaring <T> on a struct or enum without referencing T in any field or variant
E0401 Can't use generic parameters from outer item Referencing an enclosing impl<T> or fn<T> type parameter inside a nested fn or struct
E0507 Cannot move out of [T] / &T Indexing a generic slice slice[0] by value without T: Copy or T: Clone

Confusing Caller-Chosen T with Callee-Chosen Return Types (E0308)

A type parameter fn make_value<T>() -> T means the caller chooses T, and the function body must be able to produce whatever T the caller requests. Returning a fixed concrete type such as 42_i32 inside fn make_value<T>() -> T fails with E0308 because the caller might ask for make_value::<String>():

// ❌ does not compile
fn make_default<T>() -> T {
    0
}

fn main() {
    let _x: i32 = make_default();
}
error[E0308]: mismatched types
 --> src/main.rs:3:5
  |
2 | fn make_default<T>() -> T {
  |                 -       - expected `T` because of return type
  |                 |
  |                 expected this type parameter
3 |     0
  |     ^ expected type parameter `T`, found integer
  |
  = note: expected type parameter `T`
                       found type `{integer}`

Fix this in one of two ways depending on intent:

  • If the caller chooses T, constrain T with a trait capable of constructing T, such as T: Default, and return T::default().
  • If the callee chooses a single concrete hidden type, return impl Trait (fn make_default() -> impl Display) instead of <T>.

Moving Out of a Generic Slice or Reference (E0507)

With concrete primitives like &[i32], writing let first = slice[0]; works because i32 implements Copy. With an unconstrained generic slice &[T], T is not known to implement Copy, so slice[0] attempts to move ownership out of a borrowed slice:

// ❌ does not compile
fn first_owned<T>(slice: &[T]) -> T {
    slice[0]
}

fn main() {}
error[E0507]: cannot move out of `slice[_]` which is behind a shared reference
 --> src/main.rs:3:5
  |
3 |     slice[0]
  |     ^^^^^^^^ move occurs because `slice[_]` has type `T`, which does not implement the `Copy` trait
  |
help: consider borrowing here
  |
3 |     &slice[0]
  |     +

You have three ways to resolve E0507 on generic references:

  1. Return a reference (&T) instead of an owned T: fn first_ref<T>(slice: &[T]) -> Option<&T> { slice.first() }.
  2. Require T: Clone and call slice[0].clone().
  3. Require T: Copy if the function is intended only for trivially copyable types.

Using Outer Generic Parameters in Inner Items (E0401)

A nested fn or struct defined inside a generic function does not automatically inherit the outer function's type parameters:

// ❌ does not compile
fn process<T: Copy>(item: T) -> (T, T) {
    fn duplicate(x: T) -> (T, T) {
        (x, x)
    }
    duplicate(item)
}

fn main() {}
error[E0401]: can't use generic parameters from outer item
 --> src/main.rs:3:21
  |
2 | fn process<T: Copy>(item: T) -> (T, T) {
  |            - type parameter from outer item
3 |     fn duplicate(x: T) -> (T, T) {
  |                  -  ^ use of generic parameter from outer item
  |                  |
  |                  help: try introducing a local generic parameter here: `<T>`

Declare the type parameter explicitly on the inner function (fn duplicate<U: Copy>(x: U) -> (U, U)) or use a closure (let duplicate = |x: T| (x, x);), which shares the enclosing function's generic scope:

fn process<T: Copy>(item: T) -> (T, T) {
    let duplicate = |x: T| (x, x);
    duplicate(item)
}

fn main() {
    println!("{:?}", process("ok"));
}
("ok", "ok")