liveBook · Manning

Generics

As explained in Idiomatic Rust

A generic function is a function defined with type or value parameters so it can operate across multiple parameterizations.

Generics let one definition work with multiple types without duplicating code for each type. A type parameter such as `T` stands in for a concrete type in a function, struct, enum, or implementation. Rust checks these abstractions at compile time: they add no runtime overhead, though they can increase compile-time complexity and make code harder to read. section 2.1 section 2.1.2

A type parameter in a container

`struct Container<T> { value: T }` can hold different value types. With `Container { value: "Thought is free." }`, the compiler infers `T` as `&str`. But `Container { value: None }` is ambiguous: `None` does not say what the `Option` contains. An annotation such as `let c: Container<Option<String>> = Container { value: None };` supplies the missing type. section 2.1.3

A generic function cannot assume every `T` has every method. If `describe_type<T>(t: &T)` calls `t.describe()`, the compiler requires a bound identifying the trait that supplies `describe`. Write `T: SelfDescribing` inline or use a `where` clause. Each concrete type passed to the function must implement that trait. Bounds can also require multiple traits, as in `T: Clone + Debug`. section 2.2.4 section 2.1.6

fn describe_type<T>(t: &T) -> String
where
    T: SelfDescribing,
{
    t.describe()
}

The `where` form makes the function’s required behavior explicit while keeping the parameter list short. section 2.2.4

Bounds also narrow which methods are available when traits define conflicting names. Use a generic parameter when callers should supply any type offering the required behavior; avoid adding abstraction where a concrete type is clearer. If the compiler cannot infer a type, give it an annotation or specify the parameter explicitly, for example with `Container::<Option<String>>::new(None)`. section 5.2.4 section 2.1.2 section 2.1.3

Type parameters can carry compile-time distinctions even when a struct stores no value of that type. An otherwise unused parameter is an error; `PhantomData<T>` marks its intended use. Empty tag types can then distinguish specialized implementations or represent states checked through the type system. section 2.1.5 section 7.6

How Idiomatic Rust covers it

How deep it goes
Works through parameter syntax, inference failures, explicit annotations, and trait bounds, while noting compile-time and readability costs.
Examples
Uses an ambiguous `Container<Option<String>>`, a bounded description function, and tagged structs to expose compiler requirements.
What it assumes
Assumes basic Rust syntax; introduces generic parameter syntax and shows how trait bounds make shared methods available.
What you can do after
Build it from scratch: define reusable Rust types and functions, add necessary bounds, and resolve ambiguous type inference.
How it connects
Connects generics to traits, trait bounds, type inference, phantom types, and struct tagging.

Also explained in

Compare how 20 books explain generics →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.