Course contentsShow
← Rust
Lesson 528 of 1,9105. Type System and TraitsFree lesson

Generic Default Type Parameters

Design flexible APIs that provide sensible defaults while remaining fully customizable.

In this lesson, you will learn how to design flexible APIs that provide sensible default behaviors while remaining fully customizable for advanced users.

The Power of Defaults

When writing a generic struct or function, you often have a "most common" use case. However, hardcoding that type limits the flexibility of your API. Rust allows you to specify a default type parameter for generics, which acts as a fallback if the user doesn't explicitly provide a type.

Defining Defaults

You specify a default by using the = sign inside the angle brackets <T = DefaultType>. Consider a Converter struct that processes data. By default, we might want it to work with String objects, but we want to allow users to swap in custom types if they need different performance or memory characteristics:

struct Converter<T = String> {
    data: T,
}

fn main() {
    // Uses the default (String)
    let default_conv = Converter { data: String::from("Hello") };
    
    // Explicitly overrides the default with i32
    let custom_conv = Converter::<i32> { data: 42 };
}

Why Use This?

This pattern is essential for creating "progressive disclosure" APIs. It keeps the syntax clean for the 90% of users who are happy with the standard behavior, but keeps the "power user" functionality available for the 10% who need to customize the underlying types.

You see this frequently in the standard library. For example, std::collections::HashMap<K, V, S = RandomState> uses a default hasher (RandomState). Most people never need to touch the hasher, so they simply write HashMap<String, i32>. But, if a user needs a non-cryptographic, faster hasher for a specific high-performance bottleneck, they can provide their own hasher type as the third parameter.

By using default type parameters, you reduce the boilerplate for common tasks while maintaining the full expressive power of Rust’s type system for complex edge cases.

Key Takeaway: Use generic default type parameters to provide an "easy path" for common use cases while allowing users to opt into full customization when necessary.