[Pre-RFC] Keypaths in Rust
A lightweight, zero-cost abstraction library for safe, composable access to nested data structures in Rust. Inspired by Functional lenses and Swift's KeyPath system, this library provides type-safe keypaths for struct fields and enum variants for superior performance.
Inspired by Lenses: Compositional Data Access And Manipulation
The library is under development GitHub - codefonsi/rust-key-paths: ReadableKeyPath and WritableKeyPath for struct and enums in Rust
- Feature Name:
field_projection using keypaths - Start Date:
2025-09-06 - RFC PR: rust-lang/rfcs#0000
Deeply Nested Struct
Keypaths provide ergonomic way of accessing deeply nested data of any level, any type with full type-sasfety and failablity(Option/Result). Checkout the example below that demonstrate failablity, mutablity, type-safety and ergonomics of using keypaths.
let alt_path = SomeComplexStruct::scsf_fw()
.then(SomeOtherStruct::sosf_fw())
.then(OneMoreStruct::omse_fw())
.then(SomeEnum::a_case_fw());
Containers Support
The library support a handfull of widely used container types. The support for keypath is getting extended on each release.
Locks Support
use std::sync::Arc;
use keypaths_proc::Keypaths;
#[derive(Keypaths)]
#[Writable]
struct Container {
// This uses std::sync::RwLock (explicit)
std_data: Arc<std::sync::RwLock<DataStruct>>,
}
#[derive(Keypaths)]
#[Writable]
struct DataStruct {
name: String,
}
fn main() {
let container = Container { /* ... */ };
// Using generated _fr_at() for std::sync::RwLock (explicit)
Container::std_data_fr_at(DataStruct::name_r())
.get(&container, |value| {
println!("Name: {}", value);
});
}
Touple Support
Enum Support
Prior art
Shr operator is getting used to compose the keypaths.
// Alternatively, use the >> operator (requires nightly feature):
// #![feature(impl_trait_in_assoc_type)]
let keypath = SomeComplexStruct::scsf_fw()
>> SomeOtherStruct::sosf_fw()
>> OneMoreStruct::omse_fw()
>> SomeEnum::b_case_fw()
>> DarkStruct::dsf_fw();
Simplicity - The future
For read only struct the keypath provide the simplest naming with macro Keypath. i.e. Exact same field name is getting use to create keypath for each field. keeping same name for each field is challenging while enabling mutablity. Currenlty in rust with meta programming there is no prior way of knowing the root is going to be let or let mut type while in programming like swift struct defines primarily that fields is going to mutate (var) or not (let).
In recent disscussion with rust developers there might be possiblity that a field of struct say name Root can be read for a while and write for some time. with this syntax both operation are not possible that's why i have come up with a new proc macro Keypaths where the character f stands for failablity (Option / Result), r stands for readablity and w stands for writablity.
#[derive(Keypath)]
struct Person {
name: Option<String>,
result_name: Result<String, String>,
age: i32,
}
#[test]
fn test_keypath_generation() {
let person = Person {
name: Some("Alice".to_string()),
result_name: Ok("Bob".to_string()),
age: 30,
};
// Test that generated keypath methods work
let name_keypath = Person::name();
let age_keypath = Person::age();
let name_result = Person::result_name();
}
What is keypath ?
A functional component inspired by Functional Programming Lense with some additional syntactic sugar powerd by meta progtamming.
use rust_keypaths::{OptionalKeyPath, KeyPath};
struct Container {
boxed: Option<Box<String>>,
}
let container = Container {
boxed: Some(Box::new("Hello".to_string())),
};
// Unwrap Option<Box<String>> to Option<&String> automatically
let kp = OptionalKeyPath::new(|c: &Container| c.boxed.as_ref())
.for_box(); // Type automatically inferred!
if let Some(value) = kp.get(&container) {
println!("Value: {}", value);
}