pipe{i}
pipei allows writing x.pipe(f)(y, z) in place of f(x, y, z), enabling method-style chaining and reusable partial application by returning a closure over the remaining arguments.
The library similarly provides a multi-argument tap operator for side effects that returns the original value.
This project is inspired by the UMCS proposal. It requires nightly Rust for #![feature(impl_trait_in_assoc_type)].
Installation
To optimize compile time, enable only the arities you need (from 0 up to 50).
Use up_to_N features (available in multiples of five) or enable individual arity features.
[]
= "*" # default: features = ["up_to_5"]
# pipei = { version = "*", features = ["up_to_20", "31"] }
# pipei = { version = "*", features = ["0", "1", "3", "4"] }
Basic chaining
pipe passes the value to the function and returns the result.
tap passes the value for a side effect—logging, assertions, or mutation—and returns the original value.
use ;
let maybe_num = 2
.pipe
.pipe
.pipe
.pipe;
assert_eq!;
let val = 2
.tap // Immutable: passes &i32
.tap; // Mutable: passes &mut i32
assert_eq!;
Partial Application
pipe curries the first argument of a function, producing a standalone reusable function that accepts the remaining arguments.
use Pipe;
let season_pass = Discount ;
// Equivalent to the (hypothetical): let apply_discount = season_pass.apply;
let apply_discount = season_pass.pipe;
let prices = ;
let discounted = prices.map;
assert_eq!;
TapWith
tap_with takes a projection that returns an Option; if the result is Some, the side effect runs on the projected value.
This bridges the gap when a side effect’s signature does not match the receiver: the projection adapts one to the other, whether by accessing a field, calling .as_ref(), .as_bytes(), or any other transformation.
It subsumes the specialized methods from the tap crate (tap_ok, tap_dbg, etc.) using a single generic projection.
use TapWith;
let mut req = Request ;
// Simulating tap's `tap_mut` on a field
.tap_with;
// Simulating tap's `tap_err` (only tap on error)
let res = Err::
.tap_with;
assert_eq!;
// Simulating tap's `tap_dbg` (only tap in debug mode)
let final_req = req.tap_with;
assert_eq!;
Comparison with the tap crate
pipei generalizes tap to support multi-argument functions, reducing syntactic noise and simplifying control flow in pipelines involving Result or Option.
Standard Rust:
The reading order is inverted ("inside-out"): save is written first, but executes last.
save;
Using tap:
Since ? applies to the closure, the closure itself returns a Result.
This forces manual Ok wrapping and an extra ? after the pipe call.
load?
.pipe?
.pipe;
Using pipei:
The flow remains flat and ? works naturally.
load?
.pipe
.pipe;