Generics
Generics are a way to make a function or a type work for multiple types to avoid code duplication. Let's rewrite our max function to make it generic:
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b {
a
} else {
b
}
}The first thing to note is that there's a new part after the function name: this is where we declare the generic types. We declare a generic T type, : PartialOrd after it means that this T type must implement the PartialOrd trait. This is called a trait bound. We then use this T type for both of our parameters and the return type. Then, we see the same function body as the one from our non-generic function. We needed to add the trait bound because, by default, no operation is allowed on a generic type. The PartialOrd trait allows us to use the comparison operators.
We can then use this function with any type that implements PartialOrd:
println!("{}", max('a', 'z'));This is using static dispatch as opposed to dynamic dispatch, meaning that the compiler will generate a max function specific to char in the resulting binary. Dynamic dispatch is another approach that resolves the right function to call at runtime, which is less efficient.
The Option type
Generics can also be used in a type. The Option type from the standard library is a generic type, defined as such:
enum Option<T> {
Some(T),
None,
}This type is useful to encode the possibility of the absence of a value. None means no value, while Some(value) is used when there's a value.