🔑 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 crate lets you work with struct fields and enum variants as first-class values.
✨ Features
- ✅ ReadableKeyPath → safely read struct fields.
- ✅ WritableKeyPath → safely read/write struct fields.
- ✅ EnumKeyPath (CasePaths) → extract and embed enum variants.
- ✅ Composable → chain key paths together(Upcoming).
- ✅ Iterable → iterate or mutate values across collections.
- ✅ Macros → concise
readable_keypath!
, writable_keypath!
, enum_keypath!
.
📦 Installation
[dependencies]
key_paths_core = "0.3"
🚀 Examples
1. CasePaths with Enums
use key_paths_core::enum_keypath;
use key_paths_core::EnumKeyPath;
#[derive(Debug)]
struct User {
id: u32,
name: String,
}
#[derive(Debug)]
enum Status {
Active(User),
Inactive(()),
}
fn main() {
let cp = enum_keypath!(Status::Active(User));
let status = Status::Active(User {
id: 42,
name: "Charlie".to_string(),
});
if let Some(u) = cp.extract(&status) {
println!("Extracted user: {:?}", u);
}
let new_status = cp.embed(User {
id: 99,
name: "Diana".to_string(),
});
println!("Embedded back: {:?}", new_status);
}
2. Readable KeyPaths
use key_paths_core::{readable_keypath, ReadableKeyPath};
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let users = vec![
User {
name: "Akash".into(),
age: 25,
},
User {
name: "Soni".into(),
age: 30,
},
User {
name: "Neha".into(),
age: 20,
},
];
let name_key = readable_keypath!(User, name);
println!("Names:");
for name in name_key.iter(&users) {
println!("{}", name);
}
}
3. Writable KeyPaths
use key_paths_core::{writable_keypath, WritableKeyPath};
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let mut users = vec![
User {
name: "Akash".into(),
age: 25,
},
User {
name: "Soni".into(),
age: 30,
},
User {
name: "Neha".into(),
age: 20,
},
];
let age_key = writable_keypath!(User, age);
println!("Ages before:");
for age in age_key.iter(&users) {
println!("{}", age);
}
for age in age_key.iter_mut(&mut users) {
*age += 1;
}
println!("Ages after:");
for age in age_key.iter(&mut users) {
println!("{}", age);
}
}
4. Composability and failablity
use key_paths_core::{FailableReadableKeyPath};
#[derive(Debug)]
struct Engine {
horsepower: u32,
}
#[derive(Debug)]
struct Car {
engine: Option<Engine>,
}
#[derive(Debug)]
struct Garage {
car: Option<Car>,
}
fn main() {
let garage = Garage {
car: Some(Car {
engine: Some(Engine { horsepower: 120 }),
}),
};
let kp_car = FailableReadableKeyPath::new(|g: &Garage| g.car.as_ref());
let kp_engine = FailableReadableKeyPath::new(|c: &Car| c.engine.as_ref());
let kp_hp = FailableReadableKeyPath::new(|e: &Engine| Some(&e.horsepower));
let kp = kp_car.compose(kp_engine).compose(kp_hp);
let kp2 = FailableReadableKeyPath::new(|g: &Garage| {
g.car
.as_ref()
.and_then(|c| c.engine.as_ref())
.and_then(|e| Some(&e.horsepower))
});
if let Some(hp) = kp.try_get(&garage) {
println!("{hp:?}");
}
if let Some(hp) = kp2.try_get(&garage) {
println!("{hp:?}");
}
println!("{garage:?}");
}
4. Mutablity
use key_paths_core::{FailableWritableKeyPath};
#[derive(Debug)]
struct Engine {
horsepower: u32,
}
#[derive(Debug)]
struct Car {
engine: Option<Engine>,
}
#[derive(Debug)]
struct Garage {
car: Option<Car>,
}
fn main() {
let mut garage = Garage {
car: Some(Car {
engine: Some(Engine { horsepower: 120 }),
}),
};
let kp_car = FailableWritableKeyPath::new(|g: &Garage| g.car.as_ref(), |g: &mut Garage| g.car.as_mut());
let kp_engine = FailableWritableKeyPath::new(|c: &Car| c.engine.as_ref(), |c: &mut Car| c.engine.as_mut());
let kp_hp = FailableWritableKeyPath::new(|e: &Engine| Some(&e.horsepower), |e: &mut Engine| Some(&mut e.horsepower));
let kp = kp_car.compose(kp_engine).compose(kp_hp);
println!("{garage:?}");
if let Some(hp) = kp.try_get_mut(&mut garage) {
*hp = 200;
}
println!("{garage:?}");
}
🔗 Helpful Links & Resources
💡 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
📜 License
- Mozilla Public License 2.0