C.W.K.
Stream
Lesson 03 of 07 · published

Generic Functions

~11 min · traits, generics, monomorphization

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

You met generic structs in the Types track. Generic functions are the same idea applied to behavior: write the logic once, run it for every type that fits.

A type parameter on a function

fn first<T>(list: &[T]) -> &T works for a slice of any type T. The <T> after the name introduces the type parameter, exactly like a lifetime or a generic struct. At each call, the compiler figures out what T is from the arguments — you rarely write it explicitly.

Monomorphization, again

Generics cost nothing at runtime. The compiler generates a specialized copy of the function for each concrete type you actually call it with — one for i32, one for &str, each as fast as if hand-written. You get the reuse of a generic with the speed of a specialized function and no runtime dispatch.

Generic code is template code the compiler fills in. Each distinct type you use stamps out its own machine code. That's why Rust generics are 'zero-cost' — the abstraction exists only at compile time; the running binary contains concrete, specialized functions.

The catch that needs traits

A bare T is almost useless — you can move it, but you can't compare, add, or print it, because not every type supports those operations. To say 'this T can be compared,' you constrain it with a trait bound — the very next lesson. Generics give you the parameter; traits give it capabilities.

Code

A generic function works across types·rust
// A generic that only MOVES/returns values needs no bound:
fn first<T>(list: &[T]) -> &T {
    &list[0]
}

// This would NOT compile — a bare T can't be compared with >:
// fn largest<T>(list: &[T]) -> &T {
//     let mut biggest = &list[0];
//     for item in list {
//         if item > biggest { biggest = item; } // error: T may not support >
//     }
//     biggest
// }

fn main() {
    println!("{}", first(&[10, 20, 30])); // T = i32
    println!("{}", first(&["a", "b"]));   // T = &str — same code, new T
}

External links

Exercise

Write a generic fn first<T>(list: &[T]) -> &T and call it on a slice of integers and a slice of strings. Then try to write fn largest<T> that compares elements with > and read the error. Why does first compile but largest doesn't, without a bound?
Hint
first only returns a reference — it never inspects the value, so any T works. largest uses >, which not every type supports, so it needs T: PartialOrd. The next lesson adds exactly that.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.