14 个版本
0.4.0 | 2023 年 1 月 3 日 |
---|---|
0.3.3 | 2022 年 3 月 28 日 |
0.2.3 | 2022 年 3 月 8 日 |
0.1.4 | 2022 年 3 月 5 日 |
#1526 in 开发工具
5,475 下载/每月
在 profi 中使用
26KB
282 行
akin
A zero-dependency crate for writing repetitive code easier and faster
查看 语法 了解如何使用它。
为什么?
我发现自己不得不写很多重复的代码(主要是解析时匹配枚举)。
神奇的 duplicate 对我来说语法不直观,所以我决定制作自己的工具。
示例
trait Sqrt {
fn dumb_sqrt(self) -> Result<f64, &'static str>;
}
akin! {
let &int_type = [i64, u64];
let &negative_check = [
{
if self < 0 {
return Err("Sqrt of negative number")
}
},
NONE
];
let &num = [1, 2, 3, 4];
let &res = [1., 1.41, 1.73, 2.];
let &branch = {
*num => Ok(*res),
};
impl Sqrt for *int_type {
fn dumb_sqrt(self) -> Result<f64, &'static str> {
*negative_check
match self {
*branch
_ => Err("Sqrt of num not in [1, 4]")
}
}
}
}
它被扩展为
trait Sqrt {
fn dumb_sqrt(self) -> Result<f64, &'static str>;
}
impl Sqrt for i64 {
fn dumb_sqrt(self) -> Result<f64, &'static str> {
if self < 0 {
return Err("Sqrt of negative number")
}
match self {
1 => Ok(1.),
2 => Ok(1.41),
3 => Ok(1.73),
4 => Ok(2.),
_ => Err("Sqrt of num not in [1, 4]")
}
}
}
impl Sqrt for u64 {
fn dumb_sqrt(self) -> Result<f64, &'static str> {
match self {
1 => Ok(1.),
2 => Ok(1.41),
3 => Ok(1.73),
4 => Ok(2.),
_ => Err("Sqrt of num not in [1, 4]")
}
}
}
akin 的好处在于它可以自动检测每个变量在每个作用域中的值的数量,例如,“branch”将被复制 4 次(因为“num”和“res”都有 4 个值),但主函数只复制一次,因为它所拥有的所有变量都有 2 个值。
请查看存储库中的 tests/ 文件夹以获取更多示例。
语法
该软件包只提供一个宏,akin!
。语法如下
首先,声明你将使用的变量。变量名必须以 &
开头,因为这是它区分宏或真实声明的唯一方法。
请注意,变量以 ;
结尾。
let &variable = [v1, v2, v3, ...];
let &variable2 = [...];
let &code = {
...
};
然后,当所有变量都已声明后,您可以编写要复制的代码片段。
复制的次数取决于使用的变量。
let &lhs = [1, 2, 3];
let &rhs = [4, 5, 6];
println!("*lhs + *rhs = {}", *lhs + *rhs);
将被扩展为
println!("1 + 4 = {}", 1 + 4);
println!("2 + 5 = {}", 2 + 5);
println!("3 + 6 = {}", 3 + 6);
因为变量 &lhs
和 &rhs
都有 3 个值。
如你所见,&
用于声明变量,而 *
用于“解引用”到当前值。
为了方便,在声明变量时也接受范围语法,例如:0..3
和 0..=3
,它们分别等同于 [0,1,2]
和 [0,1,2,3]
。因此,上述示例也可以写成:
let &lhs = 1..=3;
let &rhs = 4..=6;
println!("*lhs + *rhs = {}", *lhs + *rhs);
目前,只有能适应 u64
的无符号整数才支持在范围中使用,例如 -10..-1
或 'a'..'c'
,这在常规 Rust 中是可以的,但在 akin
中不被接受。
如果使用的变量比另一个变量的值少,则将使用最后一个值。
akin! {
let &v1 = [c];
let &v2 = [a, b];
println!("*v1*v2");
}
展开为:
println!("ca");
println!("cb");
变量中的所有代码必须用括号括起来 {...}
。
akin! {
let &var = [-1, 1];
let &code = [
{
println!("true");
},
{
println!("false");
}
];
if *var < 0 {
*code
}
}
将展开为:
if -1 < 0 {
println!("true");
}
if 1 < 0 {
println!("false")
}
请查看存储库中的 tests/ 文件夹以获取更多示例。
NONE
NONE
是你告诉 akin
简单跳过该值并写入内容的途径。
当你想在重复的元素中包含不需要在其他元素中的元素时很有用。
akin! {
let &num = [1, 2, 3];
let &code = [
NONE,
{
.pow(2)
}
];
println!("*num^2 = {}", *num~u32*code);
// *num~u32 is necessary to ensure the type is written correctly (it would be "1 u32" otherwise)
}
联合修饰符
默认情况下,akin
在所有标识符之间放置一个空格。
let name = 5; // 'name' is an identifier
^^^^
有时,这并不理想,例如,在尝试在函数名之间进行插值时。
let &name = [1];
fn _*name()...
// Will get wrongly expanded because '_' is an identifier
fn _ 1()
为了避免这种情况,请使用连接修饰符 ~
,使下一个标识符不被分隔。
let &name = [1];
fn _~*name()... // *name is affected by the modifier
// Will get correctly expanded to
fn _1()
在字符串字面量 "..."
中,没有必要使用修饰符,因为 Rust 不将其视为标识符。
这是对 proc-macro 解析的限制,所以我怀疑它很快就会得到修复。
零依赖?真的吗?
是的,这个crate没有使用 syn
或 quote
,因为解析语法很简单,并且两者都增加了很多开销。
因此,akin
不太可能像大多数 proc-macros 那样影响编译时间,试着用一下,你自己看看!