Advanced Level

Advanced Functions and Closures

Chapter 21: Advanced Functions and Closures šŸ”§

Master function pointers, closures, and advanced function features in Rust.

Function Pointers

Basic Function Pointers

Function pointers allow you to store and pass functions as values:

fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(f(arg))
}

fn main() {
    let answer = do_twice(add_one, 5);
    println!("The answer is: {}", answer);
}

Function Pointers vs Closures

fn main() {
    // Function pointer
    let fp: fn(i32) -> i32 = add_one;
    
    // Closure
    let closure = |x: i32| x + 1;
    
    println!("Function pointer result: {}", fp(5));
    println!("Closure result: {}", closure(5));
}

Advanced Closures

Closure Traits

Closures automatically implement one of these traits:

  • Fn - borrows variables from environment immutably
  • FnMut - borrows variables from environment mutably
  • FnOnce - takes ownership of variables from environment
fn main() {
    let x = 5;
    
    // Implements Fn because it only reads x
    let equal_to_x = |z| z == x;
    println!("{}", equal_to_x(5));
    
    let mut y = 5;
    
    // Implements FnMut because it mutates y
    let mut add_to_y = |z| {
        y += z;
        y
    };
    println!("{}", add_to_y(3));
    
    let w = vec![1, 2, 3];
    
    // Implements FnOnce because it takes ownership of w
    let consume_w = move || {
        println!("{:?}", w);
        w.len()
    };
    println!("Length: {}", consume_w());
    // println!("{:?}", w); // Error: w was moved
}

Using Closures with Iterator Methods

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    
    // Closure that implements Fn
    let squared: Vec<i32> = numbers.iter().map(|x| x * x).collect();
    
    // Closure that implements FnMut
    let mut counter = 0;
    let counted: Vec<i32> = numbers.iter().map(|x| {
        counter += 1;
        x * counter
    }).collect();
    
    println!("{:?}", squared);
    println!("{:?}", counted);
}

Returning Closures

Using Box for Trait Objects

fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

fn main() {
    let closure = returns_closure();
    println!("{}", closure(5));
}

Using impl Trait

fn returns_closure_impl() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

fn main() {
    let closure = returns_closure_impl();
    println!("{}", closure(5));
}

Function Parameters

Accepting Functions and Closures

fn apply_function<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

fn add_five(x: i32) -> i32 {
    x + 5
}

fn main() {
    // Using function pointer
    let result1 = apply_function(add_five, 10);
    
    // Using closure
    let result2 = apply_function(|x| x * 2, 10);
    
    println!("Result 1: {}", result1);
    println!("Result 2: {}", result2);
}

Higher-Ranked Trait Bounds

for<'a> Syntax

fn call_with_ref<F>(f: F) -> i32
where
    F: for<'a> Fn(&'a i32) -> i32,
{
    let x = 42;
    f(&x)
}

fn main() {
    let result = call_with_ref(|y| *y + 1);
    println!("Result: {}", result);
}

Practical Examples

Example 1: Event Handler System

struct EventHandler<T> {
    handlers: Vec<Box<dyn Fn(T) -> bool>>,
}

impl<T> EventHandler<T> {
    fn new() -> EventHandler<T> {
        EventHandler {
            handlers: Vec::new(),
        }
    }
    
    fn add_handler<F>(&mut self, handler: F)
    where
        F: Fn(T) -> bool + 'static,
    {
        self.handlers.push(Box::new(handler));
    }
    
    fn handle_event(&self, event: T) -> bool {
        for handler in &self.handlers {
            if handler(event.clone()) {
                return true;
            }
        }
        false
    }
}

#[derive(Clone)]
enum Event {
    Click { x: i32, y: i32 },
    KeyPress(char),
    Resize { width: u32, height: u32 },
}

fn main() {
    let mut click_handler = EventHandler::new();
    
    // Add handlers with closures
    click_handler.add_handler(|event: Event| {
        match event {
            Event::Click { x, y } => {
                println!("Click at ({}, {})", x, y);
                true
            }
            _ => false,
        }
    });
    
    click_handler.add_handler(|event: Event| {
        match event {
            Event::KeyPress(c) => {
                println!("Key pressed: {}", c);
                c == 'q'
            }
            _ => false,
        }
    });
    
    // Handle events
    click_handler.handle_event(Event::Click { x: 100, y: 200 });
    click_handler.handle_event(Event::KeyPress('a'));
    click_handler.handle_event(Event::KeyPress('q'));
}

Example 2: Mathematical Operation Factory

#[derive(Debug, Clone, Copy)]
enum Operation {
    Add,
    Subtract,
    Multiply,
    Divide,
}

// Function that returns a closure
fn create_operation(op: Operation) -> Box<dyn Fn(f64, f64) -> Option<f64>> {
    match op {
        Operation::Add => Box::new(|a, b| Some(a + b)),
        Operation::Subtract => Box::new(|a, b| Some(a - b)),
        Operation::Multiply => Box::new(|a, b| Some(a * b)),
        Operation::Divide => Box::new(|a, b| {
            if b != 0.0 {
                Some(a / b)
            } else {
                None
            }
        }),
    }
}

// Function that accepts a function pointer
fn apply_operation(op_func: fn(f64, f64) -> f64, a: f64, b: f64) -> f64 {
    op_func(a, b)
}

// Regular functions for each operation
fn add(a: f64, b: f64) -> f64 { a + b }
fn subtract(a: f64, b: f64) -> f64 { a - b }
fn multiply(a: f64, b: f64) -> f64 { a * b }
fn divide(a: f64, b: f64) -> f64 { a / b }

fn main() {
    // Using closures
    let adder = create_operation(Operation::Add);
    let divider = create_operation(Operation::Divide);
    
    println!("5 + 3 = {:?}", adder(5.0, 3.0));
    println!("10 / 2 = {:?}", divider(10.0, 2.0));
    println!("10 / 0 = {:?}", divider(10.0, 0.0));
    
    // Using function pointers
    println!("Function pointer results:");
    println!("5 + 3 = {}", apply_operation(add, 5.0, 3.0));
    println!("5 - 3 = {}", apply_operation(subtract, 5.0, 3.0));
    println!("5 * 3 = {}", apply_operation(multiply, 5.0, 3.0));
    println!("10 / 2 = {}", apply_operation(divide, 10.0, 2.0));
}

Common Mistakes

āŒ Forgetting Closure Trait Bounds

fn apply_function<F>(f: F, x: i32) -> i32 {
    f(x) // Error: F doesn't have a known size at compile time
}

fn main() {
    let result = apply_function(|x| x * 2, 5);
    println!("{}", result);
}

āœ… Proper Closure Trait Bounds

fn apply_function<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

fn main() {
    let result = apply_function(|x| x * 2, 5);
    println!("{}", result);
}

āŒ Moving Values When Not Intended

fn main() {
    let nums = vec![1, 2, 3];
    
    let closure = || {
        println!("{:?}", nums); // Error: nums was moved into closure
    };
    
    // This won't compile because nums was moved
    // println!("{:?}", nums);
    closure();
}

āœ… Properly Borrowing Values

fn main() {
    let nums = vec![1, 2, 3];
    
    let closure = move || {
        println!("{:?}", nums); // Now nums is moved into closure
    };
    
    closure();
    // Still won't compile because nums was moved
    // println!("{:?}", nums);
}

Or:

fn main() {
    let nums = vec![1, 2, 3];
    
    let closure = |n: &Vec<i32>| {
        println!("{:?}", n);
    };
    
    closure(&nums);
    println!("{:?}", nums); // This works because we borrowed nums
}

Key Takeaways

  • āœ… Function pointers allow storing and passing functions as values
  • āœ… Closures automatically implement Fn, FnMut, or FnOnce traits
  • āœ… Use move keyword to transfer ownership into closures
  • āœ… Closures can capture variables from their environment
  • āœ… Function pointers implement all three closure traits
  • āœ… Use Box<dyn Fn()> or impl Fn() to return closures from functions
  • āœ… Higher-ranked trait bounds allow for more flexible function signatures
  • āœ… Follow Rust naming conventions and idioms for advanced functions

Ready for Chapter 22? → Macros

šŸ¦€ Rust Programming Tutorial

Learn from Zero to Advanced

Built with Next.js and Tailwind CSS • Open Source