rust-key-paths 1.13.0

Keypaths for Rust: Static dispatch implementation (rust-keypaths) and legacy dynamic dispatch (key-paths-core). Type-safe, composable access to nested data structures.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# 🔑 KeyPaths 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*.

---

## 🚀 New: Static Dispatch Implementation

**We now provide two implementations:**

### Primary: `rust-keypaths` + `keypaths-proc` (Recommended)
- **Static dispatch** - Faster performance, better compiler optimizations
-**Write operations can be faster than manual unwrapping** at deeper nesting levels
-**Zero runtime overhead** - No dynamic dispatch costs
-**Better inlining** - Compiler can optimize more aggressively
-**Functional chains for `Arc<Mutex<T>>`/`Arc<RwLock<T>>`** - Compose keypaths through sync primitives
-**parking_lot support** - Optional feature for faster locks

```toml
[dependencies]
rust-keypaths = "1.1.0"
keypaths-proc = "1.1.0"
```

### Legacy: `key-paths-core` + `key-paths-derive` (v1.6.0)
- ⚠️ **Dynamic dispatch** - Use only if you need:
  - `Send + Sync` bounds for multithreaded scenarios
  - Dynamic dispatch with trait objects
  - Compatibility with existing code using the enum-based API

```toml
[dependencies]
key-paths-core = "1.6.0"  # Use 1.6.0 for dynamic dispatch
key-paths-derive = "1.1.0"
```
---

## ✨ 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(Keypaths)]` for structs/tuple-structs and enums, `#[derive(Casepaths)]` for enums
-**Functional chains for `Arc<Mutex<T>>` and `Arc<RwLock<T>>`** - Compose-first, apply-later pattern
-**parking_lot support** - Feature-gated support for faster synchronization primitives

---

## 📦 Installation

### Recommended: Static Dispatch (rust-keypaths)

```toml
[dependencies]
rust-keypaths = "1.1.0"
keypaths-proc = "1.1.0"
```

### With parking_lot Support

```toml
[dependencies]
rust-keypaths = { version = "1.1.0", features = ["parking_lot"] }
keypaths-proc = "1.1.0"
```

### API Differences

**rust-keypaths (Static Dispatch):**
```rust
use rust_keypaths::{KeyPath, OptionalKeyPath, WritableKeyPath};

let kp = KeyPath::new(|s: &Struct| &s.field);
let opt_kp = OptionalKeyPath::new(|s: &Struct| s.opt_field.as_ref());
let writable_kp = WritableKeyPath::new(|s: &mut Struct| &mut s.field);
```
---

## 🚀 Examples

### Deep Nested Composition with Box and Enums

This example demonstrates keypath composition through deeply nested structures with `Box<T>` and enum variants:

```rust
use keypaths_proc::{Casepaths, Keypaths};

#[derive(Debug, Keypaths)]
#[Writable]
struct SomeComplexStruct {
    scsf: Box<SomeOtherStruct>,
}

impl SomeComplexStruct {
    fn new() -> Self {
        Self {
            scsf: Box::new(SomeOtherStruct {
                sosf: OneMoreStruct {
                    omsf: String::from("no value for now"),
                    omse: SomeEnum::B(DarkStruct {
                        dsf: String::from("dark field"),
                    }),
                },
            }),
        }
    }
}

#[derive(Debug, Keypaths)]
#[Writable]
struct SomeOtherStruct {
    sosf: OneMoreStruct,
}

#[derive(Debug, Casepaths)]
#[Writable]
enum SomeEnum {
    A(String),
    B(DarkStruct),
}

#[derive(Debug, Keypaths)]
#[Writable]
struct OneMoreStruct {
    omsf: String,
    omse: SomeEnum,
}

#[derive(Debug, Keypaths)]
#[Writable]
struct DarkStruct {
    dsf: String,
}

fn main() {
    use rust_keypaths::WritableOptionalKeyPath;
    
    // Compose keypath through Box, nested structs, and enum variants
    // Using .then() method (works on stable Rust)
    let keypath = SomeComplexStruct::scsf_fw()
        .then(SomeOtherStruct::sosf_fw())
        .then(OneMoreStruct::omse_fw())
        .then(SomeEnum::b_case_fw())
        .then(DarkStruct::dsf_fw());
    
    // 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();
    
    let mut instance = SomeComplexStruct::new();
    
    // Mutate deeply nested field through composed keypath
    if let Some(dsf) = keypath.get_mut(&mut instance) {
        *dsf = String::from("we can update the field of struct with the other way unlocked by keypaths");
        println!("instance = {:?}", instance);
    }
}
```

### Functional Chains for Arc<Mutex<T>> and Arc<RwLock<T>>

Compose keypaths through synchronization primitives with a functional, compose-first approach:

```rust
use std::sync::{Arc, Mutex, RwLock};
use keypaths_proc::{Keypaths, WritableKeypaths};

#[derive(Debug, Keypaths, WritableKeypaths)]
struct Container {
    mutex_data: Arc<Mutex<DataStruct>>,
    rwlock_data: Arc<RwLock<DataStruct>>,
}

#[derive(Debug, Keypaths, WritableKeypaths)]
struct DataStruct {
    name: String,
    optional_value: Option<String>,
}

fn main() {
    let container = Container::new();
    
    // Read through Arc<Mutex<T>> - compose the chain, then apply
    Container::mutex_data_r()
        .chain_arc_mutex(DataStruct::name_r())
        .get(&container, |value| {
            println!("Name: {}", value);
        });
    
    // Write through Arc<Mutex<T>>
    Container::mutex_data_r()
        .chain_arc_mutex_writable(DataStruct::name_w())
        .get_mut(&container, |value| {
            *value = "New name".to_string();
        });
    
    // Read through Arc<RwLock<T>> (read lock)
    Container::rwlock_data_r()
        .chain_arc_rwlock(DataStruct::name_r())
        .get(&container, |value| {
            println!("Name: {}", value);
        });
    
    // Write through Arc<RwLock<T>> (write lock)
    Container::rwlock_data_r()
        .chain_arc_rwlock_writable(DataStruct::name_w())
        .get_mut(&container, |value| {
            *value = "New name".to_string();
        });
}
```

**Available chain methods:**

| Method | Description |
|--------|-------------|
| `chain_arc_mutex(keypath)` | Read through `Arc<Mutex<T>>` |
| `chain_arc_mutex_optional(keypath)` | Optional read through `Arc<Mutex<T>>` |
| `chain_arc_mutex_writable(keypath)` | Write through `Arc<Mutex<T>>` |
| `chain_arc_mutex_writable_optional(keypath)` | Optional write through `Arc<Mutex<T>>` |
| `chain_arc_rwlock(keypath)` | Read through `Arc<RwLock<T>>` (read lock) |
| `chain_arc_rwlock_optional(keypath)` | Optional read through `Arc<RwLock<T>>` |
| `chain_arc_rwlock_writable(keypath)` | Write through `Arc<RwLock<T>>` (write lock) |
| `chain_arc_rwlock_writable_optional(keypath)` | Optional write through `Arc<RwLock<T>>` |

**Running the example:**
```bash
cargo run --example readable_keypaths_new_containers_test
```

### parking_lot Support

For faster synchronization with `parking_lot::Mutex` and `parking_lot::RwLock`:

```toml
[dependencies]
rust-keypaths = { version = "1.1.0", features = ["parking_lot"] }
```

```rust
use std::sync::Arc;
use parking_lot::{Mutex as ParkingMutex, RwLock as ParkingRwLock};

// Chain methods for parking_lot (feature-gated)
Container::parking_mutex_data_r()
    .chain_arc_parking_mutex(DataStruct::name_r())
    .get(&container, |value| {
        println!("Name: {}", value);
    });

Container::parking_rwlock_data_r()
    .chain_arc_parking_rwlock_writable(DataStruct::name_w())
    .get_mut(&container, |value| {
        *value = "New name".to_string();
    });
```

**Key advantage:** parking_lot locks **never fail** (no poisoning), so chain methods don't return `Option` for the lock operation itself.

**Running the example:**
```bash
cargo run --example parking_lot_chains --features parking_lot
```

---

## 🌟 Showcase - Crates Using rust-key-paths

The rust-key-paths library is being used by several exciting crates in the Rust ecosystem:

- 🔍 [rust-queries-builder]https://crates.io/crates/rust-queries-builder - Type-safe, SQL-like queries for in-memory collections
- 🎭 [rust-overture]https://crates.io/crates/rust-overture - Functional programming utilities and abstractions  
- 🚀 [rust-prelude-plus]https://crates.io/crates/rust-prelude-plus - Enhanced prelude with additional utilities and traits

---

## 🔗 Helpful Links & Resources

* 📘 [type-safe property paths]https://lodash.com/docs/4.17.15#get
* 📘 [Swift KeyPath documentation]https://developer.apple.com/documentation/swift/keypath
* 📘 [Elm Architecture & Functional Lenses]https://guide.elm-lang.org/architecture/
* 📘 [Rust Macros Book]https://doc.rust-lang.org/book/ch19-06-macros.html
* 📘 [Category Theory in FP (for intuition)]https://bartoszmilewski.com/2014/11/24/category-the-essence-of-composition/

---

## 💡 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).
* **High performance**: Only 1.46x overhead for reads, **93.6x faster** when reused, and **essentially zero overhead** for deep nested writes (10 levels)!

## ⚡ Performance

KeyPaths are optimized for performance with minimal overhead. Below are benchmark results comparing **direct unwrap** vs **keypaths** for 10-level deep nested access:

| Operation | Direct Unwrap | KeyPath | Notes                         |
|-----------|---------------|---------|-------------------------------|
| **Read (10 levels)** | **384.07 ps** | **848.27 ps** | ~464 ps absolute difference   |
| **Write (10 levels)** | **19.306 ns** | **19.338 ns** | **Essentially identical!**|

See [`benches/BENCHMARK_SUMMARY.md`](benches/BENCHMARK_SUMMARY.md) for detailed performance analysis.

---

## 🔄 Comparison with Other Lens Libraries

### Limitations of lens-rs, pl-lens, and keypath

Both **lens-rs**, **pl-lens** (Plausible Labs), and **keypath** have several limitations when working with Rust's type system, especially for nested structures:

#### keypath limitations:
1. **❌ No enum variant support**: No built-in support for enum case paths (prisms)
2. **❌ No Option<T> chain support**: Requires manual `.and_then()` composition for Option types
3. **❌ Limited container support**: No built-in support for `Result<T, E>`, `Mutex<T>`, `RwLock<T>`, or collection types
4. **❌ No failable keypaths**: Cannot easily compose through Option chains with built-in methods
5. **❌ No writable failable keypaths**: Missing support for composing writable access through Option chains
6. **❌ Limited composition API**: Less ergonomic composition compared to `.then()` chaining
7. **⚠️ Maintenance status**: May have limited active maintenance

#### pl-lens limitations:
1. **❌ No support for `Option<Struct>` nested compositions**: The `#[derive(Lenses)]` macro fails to generate proper lens types for nested structs wrapped in `Option<T>`, requiring manual workarounds
2. **❌ Limited enum support**: No built-in support for enum variant case paths (prisms)
3. **❌ No automatic failable composition**: Requires manual composition through `.and_then()` chains for Option types
4. **❌ Limited container support**: No built-in support for `Result<T, E>`, `Mutex<T>`, `RwLock<T>`, or collection types
5. **❌ Named fields only**: The derive macro only works with structs that have named fields, not tuple structs
6. **❌ No writable failable keypaths**: Cannot compose writable access through Option chains easily
7. **❌ Type system limitations**: The lens composition through Option types requires manual function composition, losing type safety

#### lens-rs limitations:
1. **❌ Different API design**: Uses a different lens abstraction that doesn't match Rust's ownership model as well
2. **❌ Limited ecosystem**: Less mature and fewer examples/documentation
3. **❌ Composition complexity**: More verbose composition syntax

### Feature Comparison Table

| Feature | rust-keypaths | keypath | pl-lens | lens-rs |
|---------|---------------|---------|---------|---------|
| **Struct Field Access** | ✅ Readable/Writable | ✅ Readable/Writable | ✅ Readable/Writable | ✅ Partial |
| **Option<T> Chains** | ✅ Built-in (`_fr`/`_fw`) | ❌ Manual composition | ❌ Manual composition | ❌ Manual |
| **Enum Case Paths** | ✅ Built-in (CasePaths) | ❌ Not supported | ❌ Not supported | ❌ Limited |
| **Tuple Structs** | ✅ Full support | ⚠️ Unknown | ❌ Not supported | ❌ Not supported |
| **Composition** |`.then()` chaining | ⚠️ Less ergonomic | ⚠️ Manual | ⚠️ Complex |
| **Result<T, E>** | ✅ Built-in support | ❌ Not supported | ❌ Not supported | ❌ Not supported |
| **Mutex/RwLock** | ✅ Built-in (`with_mutex`, etc.) | ❌ Not supported | ❌ Not supported | ❌ Not supported |
| **Arc/Box/Rc** | ✅ Built-in support | ⚠️ Unknown | ⚠️ Limited | ⚠️ Limited |
| **Collections** | ✅ Vec, HashMap, HashSet, etc. | ❌ Not supported | ❌ Not supported | ❌ Not supported |
| **Derive Macros** |`#[derive(Keypaths)]`, `#[derive(Casepaths)]` |`#[derive(Keypath)]` |`#[derive(Lenses)]` | ⚠️ Limited |
| **Deep Nesting** | ✅ Works seamlessly | ⚠️ May require workarounds | ❌ Requires workarounds | ❌ Complex |
| **Type Safety** | ✅ Full compile-time checks | ✅ Good | ✅ Good | ⚠️ Moderate |
| **Performance** | ✅ Optimized (1.46x overhead reads, near-zero writes) | ⚠️ Unknown | ⚠️ Unknown | ⚠️ Unknown |
| **Readable Keypaths** |`KeyPath` | ✅ Supported |`RefLens` | ⚠️ Partial |
| **Writable Keypaths** |`WritableKeyPath` | ✅ Supported |`Lens` | ⚠️ Partial |
| **Failable Readable** |`OptionalKeyPath` | ❌ Manual | ❌ Manual | ❌ Manual |
| **Failable Writable** |`WritableOptionalKeyPath` | ❌ Manual | ❌ Manual | ❌ Manual |
| **Zero-cost Abstractions** | ✅ Static dispatch | ⚠️ Unknown | ⚠️ Depends | ⚠️ Depends |
| **Swift KeyPath-like API** | ✅ Inspired by Swift | ⚠️ Partial | ❌ No | ❌ No |
| **Container Methods** |`with_mutex`, `with_rwlock`, `with_arc`, etc. | ❌ Not supported | ❌ Not supported | ❌ Not supported |
| **Iteration Helpers** |`iter()`, `iter_mut()` | ❌ Not supported | ❌ Not supported | ❌ Not supported |
| **Derivable References** | ✅ Full support | ✅ Full support | ❌ Not supported | ❌ Not supported |
| **Active Maintenance** | ✅ Active | ⚠️ Unknown | ⚠️ Unknown | ⚠️ Unknown |

### Key Advantages of rust-keypaths

1. **✅ Native Option support**: Built-in failable keypaths (`_fr`/`_fw`) that compose seamlessly through `Option<T>` chains (unlike keypath, pl-lens, and lens-rs which require manual composition)
2. **✅ Enum CasePaths**: First-class support for enum variant access (prisms) with `#[derive(Casepaths)]` (unique feature not found in keypath, pl-lens, or lens-rs)
3. **✅ Container types**: Built-in support for `Result`, `Mutex`, `RwLock`, `Arc`, `Rc`, `Box`, and all standard collections (comprehensive container support unmatched by alternatives)
4. **✅ Functional chains for sync primitives**: Compose keypaths through `Arc<Mutex<T>>` and `Arc<RwLock<T>>` with a clean, functional API
5. **✅ parking_lot support**: Feature-gated support for faster `parking_lot::Mutex` and `parking_lot::RwLock`
6. **✅ Zero-cost abstractions**: Static dispatch with minimal overhead (1.46x for reads, near-zero for writes) - benchmarked and optimized
7. **✅ Comprehensive derive macros**: Automatic generation for structs (named and tuple), enums, and all container types
8. **✅ Swift-inspired API**: Familiar API for developers coming from Swift's KeyPath system with `.then()` composition
9. **✅ Deep composition**: Works seamlessly with 10+ levels of nesting without workarounds (tested and verified)
10. **✅ Type-safe composition**: Full compile-time type checking with `.then()` method
11. **✅ Active development**: Regularly maintained with comprehensive feature set and documentation

### Example: Why rust-keypaths is Better for Nested Option Chains

**pl-lens approach** (requires manual work):
```rust
// Manual composition - verbose and error-prone
let result = struct_instance
    .level1_field
    .as_ref()
    .and_then(|l2| l2.level2_field.as_ref())
    .and_then(|l3| l3.level3_field.as_ref())
    // ... continues for 10 levels
```

**rust-keypaths approach** (composable and type-safe):
```rust
// Clean composition - type-safe and reusable
let keypath = Level1::level1_field_fr()
    .then(Level2::level2_field_fr())
    .then(Level3::level3_field_fr())
    // ... continues for 10 levels
    .then(Level10::level10_field_fr());
    
let result = keypath.get(&instance); // Reusable, type-safe, fast
```

---

## 🛠 Roadmap

- [x] Compose across structs, options and enum cases
- [x] Derive macros for automatic keypath generation (`Keypaths`, `Keypaths`, `Casepaths`)
- [x] Optional chaining with failable keypaths
- [x] Smart pointer adapters (`.for_arc()`, `.for_box()`, `.for_rc()`)
- [x] Container support for `Result`, `Mutex`, `RwLock`, `Weak`, and collections
- [x] Helper derive macros (`ReadableKeypaths`, `WritableKeypaths`)
- [x] Functional chains for `Arc<Mutex<T>>` and `Arc<RwLock<T>>`
- [x] `parking_lot` support for faster synchronization primitives
- [ ] Derive macros for complex multi-field enum variants
---

## 📜 License

* Mozilla Public License 2.0