Generics
| Generic functions | |
| Generic structs | |
| Fish turbo explicit args | |
| Zero-cost note |
Generic functions
A generic function takes a placeholder type instead of one fixed type. The caller decides what T stands for at each call site. First returns the opening element of any slice without caring what the slice holds.
fn first<T>(items: &[T]) -> Option<&T> {
items.first()
}
Passing integers gives back an optional integer, and passing text gives back optional text. One definition serves every element type. Bounds in a later lesson restrict what T is allowed to do.
Generic structs
Structs take the same placeholders in angle brackets after their name. Each field uses T like a normal type name. One Point shape then covers integers, floats, and any future coordinate type.
struct Point<T> {
x: T,
y: T,
}
fn main() {
let p = Point { x: 1, y: 2 };
println!("{}", p.x);
}
Both fields must share the same T within one value, so mixing integers and floats in one point fails to compile. Different points may still use different types. The compiler checks every combination separately.
Fish turbo explicit args
Type inference usually fills in T from the arguments, but some calls leave it no clues. The turbofish operator names the missing types with double colons and brackets. Collect is the classic case, since one iterator can fill many containers.
fn main() {
let nums = vec![1, 2, 3];
let doubled = nums.iter().map(|n| n * 2).collect::<Vec<i32>>();
println!("{:?}", doubled);
}
Reading the fish from the tail, collect gathers iterator items into a vector of integers. Without the annotation the compiler stops and asks which container was meant. Turbofish answers that question inline.
Zero-cost note
Generics cost nothing at run time because the compiler stamps out one copy per concrete type. The integer point and the float point below become two separate structs in the binary. Each copy runs as fast as hand written code for that type.
fn main() {
let a = Point { x: 1, y: 2 };
let b = Point { x: 1.5, y: 2.5 };
println!("{} {}", a.x, b.x);
}
This stamping process is called monomorphization, and it happens entirely at compile time. Binaries grow slightly while speed stays maximal. Generics therefore abstract without the usual run time tax.
Next: Trait bounds
Article author: Arthur Isaev