# kathy Const-evaluated, zero-cost, very simple keypath functionality for rust. Requires nightly and `env RUSTFLAGS="-Znext-solver=globally"` to work and use correctly :) ## 0 runtime cost Let's take this simply example of a struct that implements `kathy::KeyPathIndexable` (the trait that makes this all work), and modify it via `IndexMut` (which the `Keyable` macro implements for you): ```rust use kathy::Keyable; use std::ops::IndexMut; #[derive(Keyable)] struct Person { age: u16 }; fn main() { std::hint::black_box(modify( Person { age: 40 }, Person::age, 500 )); } #[inline(never)] fn modify(person: &mut Person, path: KP, field: F) where Person: IndexMut { person[path] = field; } ``` Now, to see what this actually generates, let's run it through [`cargo-show-asm`](https://crates.io/crates/cargo-show-asm) to see what this generates: ```shell $ RUSTFLAGS="-Znext-solver=globally" cargo +nightly asm demo::modify .section .text.demo::modify,"ax",@progbits .p2align 2 .type demo::modify,@function demo::modify: .cfi_startproc mov w8, #5 strh w8, [x0, #32] ret ``` With no optimizations enabled, doing only the bare minimum to ensure nothing is inlined *too* aggressively, (and assuming this is the only monomorphization of the requested fn) we get an extremely simple, basically transparent, implementation of the function. There is no runtime checking or processing - everything is completely transparent and exists only as types at runtime. And this works at arbitrary nested depths as well, even including working with `usize`-based keypaths/indices. For example: ```rust // create a keypath to the `people` field of the `Family` struct let height_kp = Family::people // extend that keypath into the first item inside `people` .idx::<0>() // extend that kp into the `dimensions` field of the first person .kp::<"dimensions">() // and finally finish the keypath off by telling it to retrieve the height. .kp::<"height">(); ``` ## usage The main building block of this crate is the [`Keyable`] derive macro - this implements [`std::ops::Index`](https://doc.rust-lang.org/std/ops/trait.Index.html) and [`std::ops::IndexMut`](https://doc.rust-lang.org/std/ops/trait.IndexMut.html) traits for all types which it is used on (along with a few other things). The specific types which `Keyable`-derived structs can be `Index`ed by, however, are unimportant. They're increasingly annoying to name the more nested they get, and are the most likely part of this library to change from version to version. The way to create these Index types, however, is by using the named helpers that are provided by the `Keyable` macro. For example, in the first example up above, a `Person::age` constant was generated by the macro, which was then used to index into a `Person`. These keypaths can then be extended by two methods: 1. `fn kp() -> _`, which takes no arguments and uses the single generic argument, a const `&'static str`, to create another, nested, keypath. 2. `fn idx() -> _`, which also takes no arguments ans uses the single generic argument, a const `usize`, to create the nested keypath. Due to the way this API works, all information about which field or index is being accessed is encoded at the type level, and every `KeyPath`-adjacent type in `kathy` is 0-sized.