🔑 KeyPaths & CasePaths in Rust
Key paths and case paths provide a safe, composable way to access and modify nested data in Rust. Inspired by Swift’s KeyPath / CasePath system, this feature rich crate lets you work with struct fields and enum variants as first-class values.
✨ Features
- ✅ Readable/Writable keypaths for struct fields
- ✅ Failable keypaths for
Option<T>chains (_fr/_fw) - ✅ Enum CasePaths (readable and writable prisms)
- ✅ Composition across structs, options and enum cases
- ✅ Iteration helpers over collections via keypaths
- ✅ Proc-macros:
#[derive(Keypath)]for structs/tuple-structs and enums,#[derive(Casepaths)]for enums
📦 Installation
[]
= "1.0.5"
= "0.9"
🎯 Choose Your Macro
#[derive(Keypath)] - Simple & Beginner-Friendly
- One method per field:
field_name() - Smart keypath selection: Automatically chooses readable or failable readable based on field type
- No option chaining: Perfect for beginners and simple use cases
- Clean API: Just call
Struct::field_name()and you're done!
use Keypath;
// Usage
let user = User ;
let name_keypath = name;
let email_keypath = email;
let name = name_keypath.get; // Some("Alice")
let email = email_keypath.get; // Some("alice@example.com")
#[derive(Keypaths)] - Advanced & Feature-Rich
- Multiple methods per field:
field_r(),field_w(),field_fr(),field_fw(),field_o(),field_fo() - Full control: Choose exactly which type of keypath you need
- Option chaining: Perfect for intermediate and advanced developers
- Comprehensive: Supports all container types and access patterns
use Keypaths;
// Usage - you choose the exact method
let user = User ;
let name_keypath = name_r;
let email_keypath = email_fr;
let name = name_keypath.get; // Some("Alice") - readable
let email = email_keypath.get; // Some("alice@example.com") - failable readable
Recommendation: Start with #[derive(Keypath)] for simplicity, upgrade to #[derive(Keypaths)] when you need more control!
Keypath vs Keypaths - When to Use Which?
| Feature | #[derive(Keypath)] |
#[derive(Keypaths)] |
|---|---|---|
| API Complexity | Simple - one method per field | Advanced - multiple methods per field |
| Learning Curve | Beginner-friendly | Requires understanding of keypath types |
| Container Support | Basic containers only | Full container support including Result, Mutex, RwLock, Weak |
| Option Chaining | No - smart selection only | Yes - full control over failable vs non-failable |
| Writable Access | Limited | Full writable support |
| Use Case | Simple field access, beginners | Complex compositions, advanced users |
When to use Keypath:
- You're new to keypaths
- You want simple, clean field access
- You don't need complex option chaining
- You're working with basic types
When to use Keypaths:
- You need full control over keypath types
- You're composing complex nested structures
- You need writable access to fields
- You're working with advanced container types
🚀 Examples
See examples/ for many runnable samples. Below are a few highlights.
Quick Start - Simple Keypath Usage
use Keypath;
Widely used - Deeply nested struct
use KeyPaths;
use ;
Iteration via keypaths
use KeyPaths;
;
/*
ABox { name: "A box", size: Size { width: 10, height: 20 }, color: Other(RGBU8(10, 20, 30)) }
ABox { name: "A box", size: Size { width: 10, height: 20 }, color: Other(RGBU8(0, 0, 0)) }
*/
📦 Container Adapters & References (NEW!)
KeyPaths now support smart pointers, containers, and references via adapter methods:
Smart Pointer Adapters
Use .for_arc(), .for_box(), or .for_rc() to adapt keypaths for wrapped types:
use Keypath;
use Arc;
let products: = vec!;
// Adapt keypath to work with Arc<Product>
let price_path = price.for_arc;
let affordable: = products
.iter
.filter
.collect;
Reference Support
Use .get_ref() and .get_mut_ref() for collections of references:
use Keypath;
let products: = hashmap.values.collect;
let price_path = price;
for product_ref in &products
Supported Adapters:
.for_arc()- Works withArc<T>(read-only).for_box()- Works withBox<T>(read & write).for_rc()- Works withRc<T>(read-only).get_ref()- Works with&Treferences.get_mut_ref()- Works with&mut Treferences
Examples:
examples/container_adapters.rs- Smart pointer usageexamples/reference_keypaths.rs- Reference collectionskey-paths-core/examples/container_adapter_test.rs- Test suite
Documentation: See CONTAINER_ADAPTERS.md and REFERENCE_SUPPORT.md
🔗 Helpful Links & Resources
- 📘 type-safe property paths
- 📘 Swift KeyPath documentation
- 📘 Elm Architecture & Functional Lenses
- 📘 Rust Macros Book
- 📘 Category Theory in FP (for intuition)
💡 Why use KeyPaths?
- Avoids repetitive
match/.chains. - Encourages compositional design.
- Plays well with DDD (Domain-Driven Design) and Actor-based systems.
- Useful for reflection-like behaviors in Rust (without unsafe).
🛠Roadmap
- Compose across structs, options and enum cases
- Derive macros for automatic keypath generation (
Keypaths,Keypath,Casepaths) - Optional chaining with failable keypaths
- Smart pointer adapters (
.for_arc(),.for_box(),.for_rc()) - Container support for
Result,Mutex,RwLock,Weak, and collections - Helper derive macros (
ReadableKeypaths,WritableKeypaths) - [] Derive macros for complex multi-field enum variants
📜 License
- Mozilla Public License 2.0