Skip to content
Development
Skill

/nw-fp-rust

Rust language-specific patterns, compiler-verified exhaustive ADTs, and Result/Option as railway-oriented programming

From plugin
nwave
635183 skills37 agents

Claude Code

$ npx -y skills add nWave-ai/nWave --skill nw-fp-rust --agent claude-code

SKILL.md

nw-fp-rust.SKILL.md
name: nw-fp-rust
agent: nw-functional-software-crafter
description: Rust language-specific patterns, compiler-verified exhaustive ADTs, and Result/Option as railway-oriented programming
user-invocable: false
disable-model-invocation: true

FP in Rust -- Functional Software Crafter Skill

Cross-references: [fp-principles](../nw-fp-principles/SKILL.md) | [fp-domain-modeling](../nw-fp-domain-modeling/SKILL.md) | [pbt-rust](../nw-pbt-rust/SKILL.md)

When to Choose Rust

  • Best for: making illegal states unrepresentable at the TYPE level, with the compiler enforcing it | systems

where a wrong state must be a compile error, not a runtime one | performance-critical domain cores

  • Not ideal for: teams needing the fastest possible prototyping loop | domains where the borrow checker's

learning cost outweighs the safety gain (short-lived scripts, throwaway tooling)

[STARTER] Quick Setup

cargo new order_service && cd order_service
# Add proptest or quickcheck as a dev-dependency for property-based testing
cargo build && cargo test

**Test runner**: `cargo test`. Add `proptest = "1"` (or `quickcheck`) under `[dev-dependencies]`.

[STARTER] Type System for Domain Modeling

Rust's `enum` is a REAL algebraic sum type with payload per variant, and the compiler REFUSES to compile a `match` that omits a case -- this is not a lint, it is a hard error. No other mainstream systems language makes illegal states this unrepresentable by default.

Choice Types (Enums with Payload)

enum PaymentMethod {
    CreditCard { number: CardNumber, expiry: ExpiryDate },
    BankTransfer { account: AccountNumber },
    Cash,
}

Record Types and Domain Wrappers (Newtype Pattern)

struct Customer {
    customer_id: CustomerId,
    customer_name: CustomerName,
    customer_email: EmailAddress,
}

struct OrderId(u64);
struct EmailAddress(String); // private field -- see smart constructor below

A tuple struct with a PRIVATE field is Rust's newtype: `EmailAddress` is a distinct type from `String` at compile time, zero runtime cost, and no way to construct one outside its own module.

[INTERMEDIATE] Smart Constructors

pub struct EmailAddress(String);

impl EmailAddress {
    pub fn new(raw: &str) -> Result<Self, ValidationError> {
        if raw.contains('@') {
            Ok(EmailAddress(raw.to_string()))
        } else {
            Err(ValidationError::InvalidEmail(raw.to_string()))
        }
    }
}

The tuple field stays private to the module (no `pub` on `String`); every construction path outside the module goes through `new`, which returns evidence (`Result`), never a bare bool.

[INTERMEDIATE] Composition Style

`?` Operator for Railway Chaining

fn place_order(raw: RawOrder) -> Result<Confirmation, OrderError> {
    let validated = validate_order(raw)?;
    let priced = price_order(validated)?;
    confirm_order(priced)
}

`?` propagates an `Err` immediately (returning it from the enclosing function) or unwraps an `Ok` and continues -- the SAME railway shape as `bind`, spelled as a postfix operator the compiler understands natively.

`Result`/`Option` Combinators

fn total_after_discount(order: &Order) -> Option<Money> {
    order
        .subtotal()
        .checked_sub(order.discount())
        .filter(|total| *total >= Money::ZERO)
}

`map`, `and_then` (`Result`'s `bind`), `filter`, `unwrap_or` -- the same vocabulary as Haskell's `Maybe`/`Either`, under names chosen for readability over category-theory precedent.

Accumulating Validation Errors

fn validate_customer(raw: &RawCustomer) -> Result<Customer, Vec<ValidationError>> {
    let name = validate_name(&raw.name);
    let email = validate_email(&raw.email);
    match (name, email) {
        (Ok(n), Ok(e)) => Ok(Customer { name: n, email: e }),
        (n, e) => Err([n.err(), e.err()].into_iter().flatten().collect()),
    }
}

`?` short-circuits on the first error; collect explicitly with a tuple match (or the `validator`/`garde` crates) when the caller needs every failure, not just the first.

[INTERMEDIATE] Effect Management

Rust has no `IO` type -- effects are ordinary function calls, and PURITY is a discipline the type signature documents by what it takes and returns, not something the compiler enforces the way Haskell's `IO` does. `async fn` marks a function that MAY suspend on I/O; ownership (`&self` vs `&mut self` vs consuming `self`) documents mutation.

// Pure domain logic -- no I/O, no interior mutability
fn calculate_discount(order: &Order) -> Discount {
    if order.lines().len() > 10 { Discount(0.1) } else { Discount(0.0) }
}

// Effectful boundary -- async, explicit dependency injection
async fn save_order(repo: &impl OrderRepository, order: Order) -> Result<(), OrderError> {
    repo.save(order).await
}

[ADVANCED] Ports as Traits, Adapters as Impls (Hexagonal Architecture)

#[async_trait]
trait OrderRepository {
    async fn find_order(&self, id: OrderId) -> Option<Order>;
    async fn save_order(&self, order: Order) -> Result<(), OrderError>;
}

struct PostgresOrderRepository { pool: PgPool }

#[async_trait]
impl OrderRepository for PostgresOrderRepository {
    async fn find_order(&self, id: OrderId) -> Option<Order> {
        sqlx::query_as("SELECT * FROM orders WHERE id = $1").bind(id.0).fetch_optional(&self.pool).await.ok()?
    }
    async fn save_order(&self, order: Order) -> Result<(), OrderError> {
        sqlx::query("INSERT INTO orders ...").execute(&self.pool).await.map(|_| ()).map_err(Into::into)
    }
}

A `trait` is the port; the domain core is generic over `impl OrderRepository` (or holds a `Box<dyn OrderRepository>`), so tests substitute an in-memory implementation with no framework needed.

[INTERMEDIATE] Testing

**Frameworks**: proptest (composable strategies, Hypothesis-like) | quickcheck (closer to the original Haskell API) | `cargo test` (built-in, table-dr

Read more
@nwave-ai
Ships withnwave

nWave supplies specialist roles and a host-led delivery workflow for software teams.

635183 skills37 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
635
Star history of nWave-ai/nWave on GitHub, a chart by star-history.com

Other skills on nwave.

nw-ab-agent-template

nw-ab-agent-template

KNOWLEDGE — the canonical agent-spec template (frontmatter + body skeleton). Reference loaded by the create/migrate procedures;…

@nwave-ai@nwave-aiInstall Skill
nw-ab-anti-patterns

nw-ab-anti-patterns

KNOWLEDGE — agent/skill/command anti-pattern catalog with fixes. Reference scanned by validate-spec; no sequence.

@nwave-ai@nwave-aiInstall Skill
nw-ab-create-agent

nw-ab-create-agent

PROCEDURE — create a NEW agent via the 5-phase workflow (ANALYZE→DESIGN→CREATE→VALIDATE→REFINE). Trigger: build a new AI agent.…

@nwave-ai@nwave-aiInstall Skill
nw-ab-examples

nw-ab-examples

KNOWLEDGE — canonical worked examples for agent creation, migration, and command optimization. Reference for the relevant…

@nwave-ai@nwave-aiInstall Skill
nw-ab-house-style

nw-ab-house-style

KNOWLEDGE — caveman-native authoring house style + by-construction guarantees (Reasoning Mandate injection, measured-gain…

@nwave-ai@nwave-aiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about nw-fp-rust.

Which coding agents can use nw-fp-rust?
nw-fp-rust installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.