| Crates.io | rust-overture |
| lib.rs | rust-overture |
| version | 0.4.0 |
| created_at | 2025-09-05 06:53:27.395129+00 |
| updated_at | 2025-09-25 17:19:33.323033+00 |
| description | A rust overture library. |
| homepage | https://github.com/codefonsi/rust-overture |
| repository | https://github.com/codefonsi/rust-overture |
| max_upload_size | |
| id | 1825158 |
| size | 234,134 |
A comprehensive functional programming library for Rust, inspired by Swift's functional programming utilities. This library provides a rich set of operators and utilities that enable functional composition, making your Rust code more expressive, maintainable, and composable.
pipe, pipe2, pipe3, etc.Result type operations with zip, zip_with, and error handlingOption type operations with map, zip, and zip_withmap, filter, reduce, etc.Add to your Cargo.toml:
[dependencies]
rust-overture = "0.3.0"
use rust_overture::pipe::{pipe, pipe2, pipe3};
// Simple pipeline
let add_one = |x: i32| x + 1;
let double = |x: i32| x * 2;
let to_string = |x: i32| x.to_string();
let process = pipe3(add_one, double, to_string);
let result = process(5); // "12"
use rust_overture::result::{zip, zip_with};
use rust_overture::pipe::pipe_throwing;
let parse_id = |s: &str| s.parse::<u32>().map_err(|_| "Invalid ID");
let parse_age = |s: &str| s.parse::<u32>().map_err(|_| "Invalid age");
let create_user = |id: u32, age: u32| format!("User {} is {} years old", id, age);
let user_result = zip_with(create_user, parse_id("123"), parse_age("25"));
// Ok("User 123 is 25 years old")
use rust_overture::options::{map, zip_with};
let get_name = |user: &User| user.name.clone();
let get_age = |user: &User| user.age;
let create_profile = |name: String, age: u32| format!("{} is {} years old", name, age);
let profile = zip_with(
create_profile,
get_name(user),
get_age(user)
);
The library includes a comprehensive fraud detection example that demonstrates the power of functional programming in real-world scenarios. Run it with:
cargo run --example practical_example
Problem: Traditional imperative code often leads to tightly coupled, monolithic functions that are difficult to reuse and test.
Solution: Functional composition allows building complex operations from simple, reusable components:
// Instead of one large function, we compose smaller ones
let validate_transaction = pipe3_throwing(
validate_amount,
validate_merchant,
validate_location,
);
Problem: Imperative code often uses nested if-else statements and early returns, making error handling verbose and error-prone.
Solution: Functional approach with Result types provides clean, composable error handling:
// Clean error propagation through the pipeline
let result = validate_transaction(transaction)
.and_then(calculate_risk)
.and_then(generate_report);
Problem: Similar validation and transformation logic gets repeated across different parts of the codebase.
Solution: Higher-order functions and currying eliminate duplication:
// Reusable validation function
let validate_range = curry(|min: f64, max: f64, amount: f64| {
amount >= min && amount <= max
});
// Can be partially applied for different ranges
let validate_amount = validate_range(10.0)(1000.0);
Problem: Large, imperative functions are hard to test and debug because they do multiple things.
Solution: Small, pure functions are easier to test and reason about:
// Each function has a single responsibility and is easily testable
#[test]
fn test_validate_amount() {
assert!(validate_amount(Transaction { amount: 100.0, .. }).is_ok());
assert!(validate_amount(Transaction { amount: -10.0, .. }).is_err());
}
Problem: Complex imperative code requires developers to track multiple variables and state changes.
Solution: Functional composition makes data flow explicit and declarative:
// Clear data transformation pipeline
let risk_assessment = transaction
|> validate_transaction
|> calculate_risk_factors
|> combine_risks
|> generate_report;
Problem: Imperative code with mutable state is difficult to parallelize safely.
Solution: Immutable data and pure functions enable safe parallelization:
// Each risk calculation is independent and can be parallelized
let risks = vec![amount_risk, location_risk, velocity_risk, device_risk]
.into_par_iter()
.map(|risk_fn| risk_fn(&transaction))
.collect::<Result<Vec<_>, _>>()?;
The functional approach often provides better performance through:
In the fraud detection example, the functional approach provides:
pipe(f) - Single function applicationpipe2(f, g) - Two-function compositionpipe3(f, g, h) - Three-function compositionpipe_throwing(f) - Error-handling compositionzip(a, b) - Combine two Results into a tuplezip_with(f, a, b) - Combine Results with a transform functionzip3, zip4, etc. - Higher-arity Result combinationsmap(f) - Transform Option valueszip(a, b) - Combine two Options into a tuplezip_with(f, a, b) - Combine Options with a transform functioncurry(f) - Curry a two-argument functioncurry3(f) - Curry a three-argument functionuncurry2(f) - Uncurry a curried functionmap(f) - Transform sequence elementsfilter(predicate) - Filter sequence elementsreduce(f, initial) - Reduce sequence to a single valueContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Inspired by Swift's functional programming utilities and the broader functional programming community.