How does Rust support functional programming paradigms? Provide examples.
Rust, while primarily a systems programming language, incorporates several features that support functional programming paradigms. Functional programming emphasizes immutability, pure functions, and the use of higher-order functions. Rust embraces these concepts and provides functional programming capabilities through various language features. Let's explore how Rust supports functional programming and provide examples:
1. Immutable variables and data:
* Rust encourages immutability by default. Variables are immutable unless explicitly marked as mutable using the `mut` keyword. This promotes the use of immutable data and reduces the chances of unintended modifications.
* Example:
```
rust`let x = 5; // Immutable variable
// x = 10; // This will result in a compile-time error since x is immutable`
```
2. Pure functions:
* Pure functions in Rust do not have side effects and always produce the same output for the same input. They rely only on the values passed as arguments and do not modify external state.
* Rust encourages pure functions by enforcing ownership and borrowing rules, preventing unintended mutations and sharing of mutable state.
* Example:
```
rust`fn add(a: i32, b: i32) -> i32 {
a + b // Pure function, only depends on the input parameters
}`
```
3. Higher-order functions and closures:
* Rust supports higher-order functions, which are functions that can accept other functions as arguments or return functions as results. This enables functional composition and allows for writing expressive and reusable code.
* Closures, also known as anonymous functions, are a concise way to define functions inline. They can capture variables from their surrounding scope, making them powerful for creating function literals.
* Example:
```
rust`fn apply\_twice<F>(f: F, x: i32) -> i32
where
F: Fn(i32) -> i32,
{
f(f(x))
}
let result = apply\_twice(|n| n * 2, 5); // Using a closure as a higher-order function
// result = 20`
```
4. Iterators and functional transformations:
* Rust provides robust support for working with iterators, which allow for processing collections of data in a functional style. Iterators in Rust can be chained together and transformed using functional methods such as `map`, `filter`, and `fold`.
* Functional transformations on iterators allow for concise and expressive code for data manipulation.
* Example:
```
rust`let numbers = vec![1, 2, 3, 4, 5];
let squares: Vec<_> = numbers.iter().map(|&n| n * n).collect();
// squares = [1, 4, 9, 16, 25]
let sum: i32 = numbers.iter().sum();
// sum = 15`
```
5. Pattern matching and algebraic data types:
* Rust's pattern matching is a powerful construct that allows you to destructure and match on different data structures, including enums and tuples. This enables concise and expressive code for handling different cases or conditions.
* Algebraic data types, especially enums, provide a way to define variant types that can represent multiple possibilities. Pattern matching with enums allows for exhaustive case handling and is a fundamental concept in functional programming.
* Example:
```
rust`enum Shape {
Circle(f64),
Rectangle(f64, f64),
}
fn calculate\_area(shape: Shape) -> f64 {
match shape {
Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
Shape::Rectangle(length, width) => length * width`
```