Generic Functions
Parameterize behavior by type.
Generic Functions is a free Learn Rust Coding lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Generic Functions
Generic functions let you write one function that works with many types instead of copying code per type. Rust replaces the placeholder type at compile time.
Imagine writing one largest for integers and another for chars. Generics collapse those into a single definition with no runtime cost.
Type Parameters
A generic function declares a type parameter inside angle brackets after the name. The name T is a convention, but any CamelCase identifier works.
The parameter can then appear in arguments and the return type, standing in for whatever concrete type the caller uses.
fn first<T>(pair: (T, T)) -> T {
pair.0
}A Simple Identity Function
This program defines a generic echo that returns its argument unchanged. It is called once with an integer and once with a string slice.
The compiler generates a separate concrete version for each type that is actually used.
fn echo<T>(value: T) -> T {
value
}
fn main() {
println!("{}", echo(42));
println!("{}", echo("hi"));
}Monomorphization
Rust uses monomorphization: at compile time it produces a specialized copy of the function for every concrete type used. There is no boxing or virtual dispatch.
The result is generic code that runs as fast as hand-written, type-specific code.
Generics Need Constraints
Inside a generic function you can only use operations that every possible type supports. A bare T cannot be added, compared, or printed.
This code fails to compile because T might not implement comparison. We will fix it with a trait bound next.
fn larger<T>(a: T, b: T) -> T {
if a > b { a } else { b }
}Adding a Trait Bound
To compare values we constrain T with PartialOrd. The bound promises the type supports the > operator.
We also add Copy so the values can be returned without moving issues for simple types like integers.
fn larger<T: PartialOrd + Copy>(a: T, b: T) -> T {
if a > b { a } else { b }
}Calling the Bounded Function
Now the function works for any type implementing both traits. Integers and floats both satisfy PartialOrd + Copy.
The same source code, monomorphized into two versions, prints results for each call.
fn larger<T: PartialOrd + Copy>(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn main() {
println!("{}", larger(3, 9));
println!("{}", larger(2.5, 1.0));
}Multiple Type Parameters
A function can declare several type parameters. Each is independent, so the two arguments may have different types.
Here pair accepts any T and any U and returns them as a tuple.
fn pair<T, U>(a: T, b: U) -> (T, U) {
(a, b)
}Printing Generic Values
To print a generic value with {} the type must implement Display. We add that bound so any printable type can be passed.
This labels a value and returns it, demonstrating a bound used purely for formatting.
use std::fmt::Display;
fn announce<T: Display>(label: &str, value: T) {
println!("{}: {}", label, value);
}
fn main() {
announce("count", 7);
announce("name", "Ada");
}Turbofish Syntax
Sometimes the compiler cannot infer T from arguments alone. The turbofish ::<Type> lets you specify it explicitly at the call site.
It is most common with methods like parse and collect where the return type is ambiguous.
fn main() {
let n = "42".parse::<i32>().unwrap();
println!("{}", n + 1);
}Generics Over References
Generic parameters work with references too. Constraining by reference avoids requiring Copy when you only need to read values.
This longest_str-style helper borrows two slices and returns one without taking ownership.
fn pick<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> &'a T {
if a > b { a } else { b }
}Quick Check
Test your understanding of generic functions in Rust.
Recap
Generic functions use type parameters in angle brackets to work across many types with zero runtime cost via monomorphization.
Operations on a generic type require trait bounds like PartialOrd, Copy, or Display. Use the turbofish when inference cannot determine the type.
Frequently asked questions
Is the “Generic Functions” lesson free?
Yes — the full text of “Generic Functions” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Generic Functions”?
Parameterize behavior by type. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Generic Functions” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.