Generics
Generic functions
You can parameterize functions by type with angle brackets:
fn identity<T>(x: T) -> T {
x
}
let n: i32 = identity(42);
let s: &str = identity("hello");In C#: Like T Identity<T>(T x) => x;. Rust generics are monomorphized at compile time: a distinct version of the function is generated for each concrete type.
Generic structs
struct Point<T> {
x: T,
y: T,
}
let p_int = Point { x: 1, y: 2 };
let p_float = Point { x: 1.0, y: 2.0 };In C#: Like struct Point<T> { public T X; public T Y; }. No boxing; no runtime type info.
Generic enums
Option<T> and Result<T, E> are generic enums you use every day:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}In C#: Similar to generic classes; Rust has no runtime generics (no reification), so no typeof(T) at runtime.
Multiple type parameters
fn pair<T, U>(a: T, b: U) -> (T, U) {
(a, b)
}In C#: Like (T, U) Pair<T, U>(T a, U b).
Constraining with traits (trait bounds)
Generics can be constrained so the type implements a trait (see Traits module):
fn print_it<T: std::fmt::Display>(x: T) {
println!("{}", x);
}In C#: Like where T : IFormattable or interfaces. Rust uses traits instead of interfaces.
Key takeaway
Use <T> (and <T, E>, etc.) for generic types. Rust monomorphizes at compile time, so there is no runtime cost. Use trait bounds when you need to call methods on T.