#macro #repeat #substitute #proc-macro #substitution #for #for-each

dry

Rust 宏,用于代码的惯用去重。当 macro_rules! 过于强大且笨拙时使用。

2 个版本

0.1.1 2022年5月21日
0.1.0 2022年5月21日

2355开发工具

Download history 486/week @ 2024-03-11 274/week @ 2024-03-18 326/week @ 2024-03-25 39/week @ 2024-04-01 21/week @ 2024-04-08 20/week @ 2024-04-15 72/week @ 2024-04-22 46/week @ 2024-04-29 12/week @ 2024-05-06 136/week @ 2024-05-13 43/week @ 2024-05-20 82/week @ 2024-05-27 22/week @ 2024-06-03 24/week @ 2024-06-10 23/week @ 2024-06-17 23/week @ 2024-06-24

94 每月下载次数
5 crate 中使用

MIT/Apache

18KB
227

dry — 别重复自己

Latest version Documentation License: MIT/Apache 2.0

Rust 宏,用于代码的惯用去重。当 macro_rules! 过于强大且笨拙时使用。

[dependencies]
dry = "0.1.1"

用法

macro_for!

您熟悉可靠的 for 循环

for number in [1, 2, 3, 4, 5] {
  println!("{}", number);
}

使用 macro_for! 在编译时迭代标记

macro_for!($Struct in [A, B, C, D, E] {
  struct $Struct {
    many_fields: bool,
    so_many_fields: bool,
    impossibly_many_fields: bool,
  }
});

与使用 macro_rules! 相比

macro_rules! my_struct {
  ($Struct:ident) => {
    struct $Struct {
      many_fields: bool,
      so_many_fields: bool,
      impossibly_many_fields: bool,
    }
  };
}
my_struct!(A);
my_struct!(B);
my_struct!(C);
my_struct!(D);
my_struct!(E);

请参阅 示例 以获取更多详细信息。

macro_wrap!

允许您在宏调用非法的位置(例如结构体字段、枚举情况、match 分支)中使用此 crate 中的其他宏。

包装最接近的宏调用位置的上层语法树祖先,然后就可以使用了

macro_wrap!(match x {
  // ↓ can't usually call macros here, but `macro_wrap!` makes it work
  macro_for!($Variant in [A, B, C, D, E] {
    Enum::$Variant => 1,
  })
})

特性

nightly 特性(默认禁用)启用使用不稳定的 proc_macro_span rustc 功能。它启用更好的语法检查(禁止 "$" 和替换变量名之间的空格)并在错误时发出更多的源代码提示(尽管宏的快速修复功能即使在 nightly 版本中也不可用)。

如果您正在运行 Rust nightly,您可以启用它

[dependencies]
dry = { version = "0.1.1", features = ["nightly"] }

关于此 crate

依赖关系

唯一的依赖是 proc-macro-error,用于在不同 Rust 版本之间提供甜美的、友好的错误消息。反过来,它依赖于 quoteproc-macro2。然而,我们完全不依赖 syn,因此 dry 应该在编译时间上非常轻量。

注意

如果可能,您应该尝试使用循环、特性和泛型等抽象。但是当无法这样做时,dry 会尽可能使避免重复变得不那么痛苦和愉快。

先前的艺术

For Each 循环

  • duplicate:目前最受欢迎的,其工作方式与普通的for循环类似,具有元组解构,但语法非常陌生。它提供了一种属性语法,可以避免一些嵌套,但在我看来,这牺牲了清晰度。函数式语法可以在属性语法有效的地方使用,另外,“$”前缀的标识符在Rust中是无效的,因此不可能在属性语法中实现。
  • akin:通过隐式for comprehension扩展了let语法。它避免了大量替换变量的嵌套,但在我看来,它感觉过于神奇,不太适合大多数Rust代码库。
  • ct-for:几乎达到了目的!然而,它使用了in ... do语法,而不是Rust程序员更熟悉的in ... {}语法。这也使得编辑器更难正确缩进循环体。最后,ctct_for中的描述性不是很强。

所有这些都在循环体内部使用裸标识符而不是“$”前缀标识符(如drymacro_rules!),这使得在循环体内部使用宏与标准语言特性之间的区别变得清晰。据我所知,它们都不支持复制结构体字段、枚举案例或匹配分支。

重复N次

  • repeated:虽然不是同一件事,但它提醒作者注意宏调用位置(例如,匹配分支等)问题以及解决该问题的一种方法。事实上,一个具有许多分支的外部crate中枚举的match触发了对dry的初始开发!
  • seq_macro:启发了在macro_for!中使用的语法。如果你想在编译时迭代一系列数值或字符值而不是令牌列表,这是一个很好的选择。

许可证

dry根据MIT许可证Apache 2.0许可证进行许可,由您选择。

除非您明确声明,否则根据Apache-2.0许可证定义,您有意提交的工作中的任何贡献,都应如上所述进行双重许可,不附加任何额外条款或条件。

贡献

如果您想看到任何东西被修复或添加,请打开一个pull request,或者如果您不确定如何开始,请创建一个问题

通往1.0的道路

  • 类似for的语法。
  • 有用的编译器错误信息和提示,模仿rustc的等效运行时构造的错误。
  • 用于宏调用非法的包装器(例如,结构体字段、枚举案例、匹配分支):macro_wrap
  • 修复在最后一个}之后添加内容被忽略的bug。应该是一个错误。
  • 更好的文档
  • 测试
  • 支持使用类似元组解构的语法支持多个替换变量
  • 支持在替换中包含逗号,通过括号包装(并支持括号,通过双写它们)
  • 确定最小Rust版本
  • 使用作用域替换变量进行嵌套(目前替换变量是向外展开,而不是像在常规 for 循环中期望的向内展开)
  • macro_let 宏用于惯用替换(在不需要语法参数的情况下替换 macro_rules!
  • 调查在循环体中将替换与语法元素连接起来?例如标识符($variable~_suffix),或运算符(variable $op~= change)。这旨在简单情况下作为 macro_rules! 的直接替代。它如何解决这个问题?参见 paste crate。
  • macro_wrap 是否也能在 crate 外部展开宏?可能不行,但让我们调查一下。也许如果我们不能自动执行它,我们可以让其他宏 crate 插入其中。
  • 如何处理具有重复项目组的替换?duplicate 通过他们所说的 参数化替换 解决了这个问题

依赖关系

~140KB