Functional Programming in Rust: From A to Z

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

Functional Programming in Rust: From A to Z



The Rust language, known for its safe memory management and high performance, also offers powerful tools for functional programming. Unlike purely functional languages (Haskell), Rust allows flexible combination of imperative and functional styles. In this article, we will break down the key concepts of functional programming in Rust: from basic closures to advanced monadic patterns. The material will be useful for both beginners and experienced developers who want to write more concise and reliable code.



1. Basics: Immutability and Variables



Functional programming begins with immutability. In Rust, variables are immutable by default. This is a key difference from many languages.



// Immutable variable (by default)let x = 10;// x = 20; // Compilation error!

// Mutable variable (explicit)let mut y = 5;y = 15; // Works


Using let without mut guarantees that the value will not change after initialization. This reduces the number of side effects and makes the code more predictable. In a functional style, it is recommended to prefer immutable variables, creating new values instead of modifying old ones.



2. Closures — Anonymous Functions



Closures are anonymous functions that can capture variables from their environment. In Rust, they are implemented through the Fn, FnMut, and FnOnce traits.



2.1. Syntax and Variable Capture



// Simple closurelet add_one = |x: i32| -> i32 { x + 1 };println!("{}", add_one(5)); // 6

// Capturing a variable from the environmentlet base = 10;let add_base = |x| x + base; // base is captured by reference (&)println!("{}", add_base(20)); // 30

// Capture with mutation (FnMut)let mut counter = 0;let mut increment = || { counter += 1; };increment();increment();println!("{}", counter); // 2

// Capture with consumption (FnOnce)let name = String::from("Rust");let consume = || { drop(name); };consume();// println!("{}", name); // Error: name has been moved


2.2. Closures as Function Arguments



Closures are often passed to higher-order functions such as map, filter, and fold.



let numbers = vec![1, 2, 3, 4, 5];

// Using a closure with filterlet evens: Vec<i32> = numbers .into_iter() .filter(|&x| x % 2 == 0) .collect();

println!("{:?}", evens); // [2, 4]


3. Iterators and Transformation Chains



Iterators are the heart of functional programming in Rust. They are lazy (computations happen only when collect() or for is called) and allow building transformation chains.



3.1. Basic Iterator Adapters



let data = vec![1, 2, 3, 4, 5, 6];

// Chain: filter -> map -> take -> collectlet result: Vec<i32> = data .iter() .filter(|&x| x % 2 == 0) // keep even numbers .map(|x| x * 2) // double them .take(2) // take the first two .collect();

println!("{:?}", result); // [4, 8]


3.2. Fold and Reduction



fold is the equivalent of reduce in other languages. It accumulates a result by iterating over all elements.



let numbers = [1, 2, 3, 4, 5];

// Sum of all numberslet sum = numbers.iter().fold(0, |acc, &x| acc + x);println!("Sum: {}", sum); // 15

// Factoriallet factorial = (1..=5).fold(1, |acc, x| acc * x);println!("5! = {}", factorial); // 120


3.3. Lazy Infinite Iterators



// Infinite iterator, take the first 10let first_ten: Vec<i32> = (0..)    .filter(|x| x % 2 == 0)    .map(|x| x * x)                

Blogs

Book Recommendations