liveBook · Manning

Composition

As explained in Write Powerful Rust Macros

Function composition combines functions so that the output of one becomes the input of another.

Function composition combines small functions into a larger function: each output becomes the next input. In Rust, this supports applications built from simple, understandable, testable pieces rather than object interactions. section 2.2.5

Compose a typed pipeline

Start with `add_one: i32 -> i32`, then `stringify: i32 -> String`, then a partially applied `prefix_with("Result: ")`. Calling the resulting function with `5` runs those stages in order and produces a prefixed string. Every stage must accept the preceding stage’s output; additional compatible stages can be added. section 2.2.5

Listing 2.16 A function to compose two functions
fn compose_two<FIRST, SECOND, THIRD, F, G>(f: F, g: G)
    -> impl Fn(FIRST) -> THIRD        #1
    where F: Fn(FIRST) -> SECOND,     #2
          G: Fn(SECOND) -> THIRD      #3
{
    move |x| g(f(x))    #4
}

fn main() {
    let two_composed_function = compose_two(
        compose_two(add_one, stringify),
        prefix_with("Result: ")
    );                             
} #5

`compose_two` makes the core operation explicit: its bounds connect `Fn(FIRST) -> SECOND` to `Fn(SECOND) -> THIRD`, and its returned closure evaluates `g(f(x))`. Larger pipelines can be built by nesting this two-function form. section 2.2.5

A declarative `compose!` macro removes the nesting. Its base case returns one function unchanged; its recursive case combines the first expression with the composition of the remaining expressions. Comma-separated input can also be written with arrows when that reads more clearly. section 2.2.5

Listing 5.7 Our compose macro entry point
#[proc_macro]
pub fn compose(item: TokenStream) -> TokenStream {
    let ci: ComposeInput = parse_macro_input!(item);      #1

    quote!(
        {
            fn compose_two<FIRST, SECOND, THIRD, F, G>(first: F, second: G)
            -> impl Fn(FIRST) -> THIRD
            where
                F: Fn(FIRST) -> SECOND,
                G: Fn(SECOND) -> THIRD,
            {
                move |x| second(first(x))
            } #2
            #ci
        }                   
    ).into()
}

A function-like procedural macro can provide dot-separated composition syntax such as `compose!(add_one . add_one . stringify)`. It parses custom input, emits a local `compose_two`, and wraps the generated declarations and expression in a block so the invocation remains one value for a `let` binding. section 5.3

Composition is also a macro use case when fluent syntax is difficult or impossible in ordinary Rust. A function-like macro is suitable for this custom expression input, whereas derive and attribute macros expect valid Rust items such as structs, enums, or functions. section 5.3 section 2.2

How Write Powerful Rust Macros covers it

How deep it goes
Works from typed two-function composition through recursive macro expansion and procedural parsing, without detailing the complete parser implementation.
Examples
Uses `add_one`, `stringify`, and `prefix_with` in comma-, arrow-, and dot-separated composition pipelines.
What you can do after
Build it from scratch with a generic `compose_two` closure and a recursive macro, or apply it through generated dot syntax.
How it connects
Connects function composition with partial application, declarative macros, function-like procedural macros, DSLs, and Rust closures.

Also explained in

Compare how 11 books explain composition →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Write Powerful Rust Macros. The section links point to the parts of the book it draws on.