1 个不稳定版本
新 0.1.0 | 2024 年 8 月 21 日 |
---|
#844 在 Rust 模式
12KB
156 行
kathy
kathy 是为 Rust 提供常量评估、零成本、非常简单的键路径功能的库。
需要 nightly 和 env RUSTFLAGS="-Znext-solver=globally"
才能正确运行和使用 :)
0 运行时成本
让我们以一个实现 kathy::KeyPathIndexable
特性(使这一切成为可能的特性)的 struct 的简单例子为例,并通过 IndexMut
(该宏为你实现的)来修改它。
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<KP, F>(person: &mut Person, path: KP, field: F)
where
Person: IndexMut<KP, Output = F>
{
person[path] = field;
}
现在,为了看到它实际上生成了什么,让我们通过 cargo-show-asm
来运行它,看看生成了什么。
$ 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
没有启用优化,只做最基本的工作以确保没有东西被内联得太激进(并假设这是请求的 fn 的唯一单态化)。
没有运行时检查或处理 - 一切都是完全透明的,仅在运行时作为类型存在。
这也可以在任意嵌套深度上工作,甚至包括使用基于 usize
的键路径/索引。例如
// 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">();
用法
这个 crate 的主要构建块是 Keyable
derive 宏 - 它为它所使用的所有类型实现 std::ops::Index
和 std::ops::IndexMut
特性(以及一些其他东西)。
然而,Keyable派生struct可以被Index索引的具体类型并不重要。随着嵌套层次的增加,这些类型命名变得越来越令人烦恼,并且很可能是这个库在各个版本之间变化最大的部分。
然而,创建这些Index类型的办法是使用由Keyable宏提供的命名辅助工具。例如,在上面的第一个例子中,一个Person::age常量是由宏生成的,然后用来索引到Person。
这些键路径可以通过两种方法进行扩展
fn kp<const FIELD: &'static str>() -> _
,它不接受任何参数,并使用单个泛型参数,一个const&'static str
,来创建另一个嵌套的键路径。fn idx<const I: usize>() -> _
,它也不接受任何参数,并使用单个泛型参数,一个constusize
,来创建嵌套的键路径。
由于此API的工作方式,关于正在访问的字段或索引的所有信息都在类型级别上编码,并且kathy中所有与KeyPath相邻的类型都是0大小的。
依赖关系
~295–750KB
~18K SLoC