Rust Tutorial
Estimated reading: 3 minutes 275 views

🦀 Rust – Generics, Traits & Closures: Write Reusable and Flexible Code

Introduction – Why Learn Generics, Traits & Closures in Rust?

In Rust, generics allow you to write type-agnostic code, traits define shared behavior, and closures provide flexible inline functions. Together, they form the backbone of Rust’s type safety, abstraction, and functional programming power—without sacrificing performance.

In this guide, you’ll learn:

  • How to write and use generic functions and structs
  • How traits define interfaces and enforce contracts
  • How closures work and capture environment
  • Code examples with output and detailed explanations

Generics – Write Type-Agnostic Code

Generic Function Example

fn print_value<T: std::fmt::Debug>(val: T) {
    println!("{:?}", val);
}

fn main() {
    print_value(42);
    print_value("Rust");
}

Explanation:

  • T is a generic type parameter
  • T: Debug ensures val can be printed with {:?}

Output:

42
"Rust"

Generic Struct Example

struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let int_point = Point { x: 1, y: 2 };
    let float_point = Point { x: 1.1, y: 2.2 };

    println!("({}, {})", int_point.x, int_point.y);
    println!("({}, {})", float_point.x, float_point.y);
}

Output:

(1, 2)
(1.1, 2.2)

Generic structs are instantiated with different types (i32, f64, etc.)


Traits – Define Shared Behavior

Traits are like interfaces—they define required method signatures.

Trait and Implementation Example

trait Speak {
    fn speak(&self);
}

struct Dog;
struct Cat;

impl Speak for Dog {
    fn speak(&self) {
        println!("Woof!");
    }
}

impl Speak for Cat {
    fn speak(&self) {
        println!("Meow!");
    }
}

fn main() {
    let d = Dog;
    let c = Cat;
    d.speak();
    c.speak();
}

Output:

Woof!
Meow!

Using Trait Bounds in Generics

fn describe<T: Speak>(item: T) {
    item.speak();
}

This function can accept any type that implements the Speak trait.


Closures – Anonymous Functions with Environment Capture

Basic Closure Example

fn main() {
    let add = |a, b| a + b;
    println!("Sum = {}", add(3, 4));
}

Output:

Sum = 7

Closures automatically infer parameter and return types.


Closure Capturing Environment

fn main() {
    let base = 10;
    let add_base = |x| x + base;
    println!("Result: {}", add_base(5));
}

Output:

Result: 15

Closures can borrow, mutably borrow, or take ownership of captured variables depending on usage.


Closure Syntax Variants

FormDescription
`x
`x: i32
`movex

Trait + Generic + Closure: Real-World Combo

fn apply_twice<F: Fn(i32) -> i32>(func: F, val: i32) -> i32 {
    func(func(val))
}

fn main() {
    let double = |x| x * 2;
    println!("Result: {}", apply_twice(double, 3));
}

Output:

Result: 12

Summary – Recap & Next Steps

Rust empowers you to write flexible, efficient, and safe code using generics, traits, and closures. These tools unlock expressive patterns while preserving compile-time guarantees.

Key Takeaways:

  • Generics: Write code that works for any type
  • Traits: Define and enforce shared behavior
  • Closures: Create inline, environment-aware functions
  • Use trait bounds to combine generics and traits
  • Closures simplify callbacks, iterations, and transformations

Next: Explore Rust – Input / Output & File Handling to work with real-world data streams.


FAQs


Can traits have default method implementations?
Yes. Define a method body inside the trait itself, and implementing types can override it if needed.


How are generics different from trait objects (dyn Trait)?
Generics are monomorphized (resolved at compile time); dyn Trait enables runtime polymorphism (dynamic dispatch).


Can closures mutate their environment?
Yes. Use mut and move if you want the closure to take or modify ownership of captured variables.


Can I return a closure from a function?
Yes, but it requires either impl Fn or boxing: Box<dyn Fn()>.


Share Now :
Share

Rust Generics, Traits & Closures

Or Copy Link

CONTENTS
Scroll to Top