Functional Programming in Rust: Approaches and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Functional Programming in Rust

Rust is a multi-paradigm language that, alongside imperative and object-oriented styles, actively supports functional programming. Built-in features such as closures, iterators, a powerful type system, and immutability by default allow you to write concise, safe, and expressive code. In this article, we will break down the key concepts of the functional approach in Rust and show how to apply them in practice.



Fundamentals of the Functional Approach: Immutability and Pure Functions

Functional programming is built on two pillars: data immutability and pure functions. Rust encourages immutability — by default, all variables are immutable. To change a value, you need to explicitly specify mut.

fn main() {    let x = 5; // immutable variable    // x = 6; // compilation error    let mut y = 10;    y += 5; // allowed, because y is mutable    println!("x = {}, y = {}", x, y);}

A pure function has no side effects and always returns the same result for the same arguments. Rust allows you to write such functions but does not force you to — you can freely mix styles.

fn add(a: i32, b: i32) -> i32 {    a + b // pure function}


Closures — Anonymous Functions

Closures in Rust are anonymous functions that can capture variables from the surrounding scope. They are defined using the syntax |parameters| body. Closures come in three types based on the capture method: by reference (&T), by mutable reference (&mut T), and by value (T).

Simple Closure Example

fn main() {    let factor = 2;    let multiply = |x: i32| x * factor;    println!("{}", multiply(10)); // 20}

Closure with Mutable Capture

fn main() {    let mut counter = 0;    let mut increment = || {        counter += 1;        counter    };    println!("{}", increment()); // 1    println!("{}", increment()); // 2}

Closures are widely used with iterators and for passing behavior to higher-order functions.



Iterators and Functional Chains

Iterators in Rust are one of the most powerful functional abstractions. Instead of loops, you can build chains of methods like map, filter, fold, and others. Iterators are lazy — computations only happen when a consumer (e.g., collect or for_each) is called.

Example: Filtering and Transformation

fn main() {    let numbers = vec![1, 2, 3, 4, 5, 6];    let result: Vec<i32> = numbers        .iter()        .filter(|&x| x % 2 == 0) // keep even numbers        .map(|x| x * x)          // square them        .collect();    println!("{:?}", result); // [4, 16, 36]}

Fold — Value Accumulation

fn main() {    let numbers = vec![1, 2, 3, 4];    let sum = numbers.iter().fold(0, |acc, x| acc + x);    println!("Sum: {}", sum); // 10}

Iterators make code declarative: you describe what needs to be done, not how. This reduces the likelihood of errors and improves readability.



Pattern Matching

The match construct is a central element of the functional style in Rust. It allows you to destructure complex data structures, handle Option and Result variants, and write exhaustive checks.

Basic Example

fn describe_number(n: i32) -> &'static str {    match n {        0 => "zero",        1 | 2 => "small number",        3..=10 => "medium number",        _ if n < 0 => "negative",                

Blogs

Book Recommendations