12 个版本

0.2.0 2023 年 5 月 13 日
0.1.4 2023 年 5 月 10 日
0.1.3 2022 年 10 月 25 日
0.1.2 2022 年 7 月 8 日
0.0.1 2019 年 9 月 5 日

过程宏 中排名第 1924

Download history 49162/week @ 2024-03-14 51299/week @ 2024-03-21 40940/week @ 2024-03-28 49995/week @ 2024-04-04 49274/week @ 2024-04-11 59567/week @ 2024-04-18 58823/week @ 2024-04-25 53446/week @ 2024-05-02 51750/week @ 2024-05-09 54965/week @ 2024-05-16 54907/week @ 2024-05-23 60700/week @ 2024-05-30 54047/week @ 2024-06-06 49467/week @ 2024-06-13 66946/week @ 2024-06-20 49526/week @ 2024-06-27

每月下载量 230,685
215 个 Crates 中使用(通过 macro_rules_attribute

MIT 许可证 MIT

11KB
243

::macro_rules_attribute

在属性或 derive 位置使用声明式宏。

macro_rules! my_fancy_decorator { /**/ }

#[apply(my_fancy_decorator!)]
struct Foo { /**/ }
macro_rules! MyFancyDerive { /**/ }

#[derive(MyFancyDerive!)]  /* using this crate's `#[derive]` attribute */
struct Foo { /**/ }

Repository Latest version Documentation MSRV unsafe forbidden License CI

动机

点击查看

macro_rules! 宏可以非常强大,但它们的调用位置可利用性有时并不太好,尤其是在装饰项目定义时。

确实,比较

foo! {
    struct Struct {
        some_field: SomeType,
    }
}

#[foo]
struct Struct {
    some_field: SomeType,
}
  1. 前者扩展性不好,因为它会导致 向右漂移和“过多的”花括号

  2. 但另一方面,后者需要为编译器设置一个专门的 crate,一个 proc-macro crate。而且 99% 的情况下这将引入 ::syn::quote 依赖项,它们具有 不可忽视的编译时间开销(首次编译时)。

    • 注意:这些 crate 是一项优秀的技术,可以创建非常强大的宏。当宏的逻辑非常复杂,以至于在作为 macro_rules! 宏实现时需要递归的 tt 消费者时,肯定需要使用过程宏。

      任何涉及 ident 生成/推导的内容,例如,通常需要过程宏,除非它足够简单以至于可以由 ::paste 处理。


解决方案

使用这个crate的#[apply]#[derive]属性,现在可以使用proc_macro_attribute语法来应用一个macro_rules!

#[macro_use]
extern crate macro_rules_attribute;

macro_rules! foo {
    //
    # ( $($tt:tt)* ) => ()
}

macro_rules! Bar {
    //
    # ( $($tt:tt)* ) => ()
}

#[apply(foo)]
#[derive(Debug, Bar!)]
struct Struct {
    some_field: SomeType,
}
#
# fn main() {}

甚至无需依赖::quote::syn::proc-macro2,以实现快速的编译时间

示例

点击查看

更优雅的derive

#[macro_use]
extern crate macro_rules_attribute;

// Easily define shorthand aliases for "derive groups"
derive_alias! {
    #[derive(Eq!)] = #[derive(Eq, PartialEq)];
    #[derive(Ord!)] = #[derive(Ord, PartialOrd, Eq!)];
    #[derive(Copy!)] = #[derive(Copy, Clone)];
    #[derive(StdDerives!)] = #[derive(Debug, Copy!, Default, Ord!, Hash)];
}

/// Strongly-typed newtype wrapper around a `usize`, to be used for `PlayerId`s.
#[derive(StdDerives!, Into!, From!)]
pub
struct PlayerId /* = */ (
    pub usize,
);

// You can also fully define your own derives using `macro_rules!` syntax
// (handling generic type definitions may be the only finicky thing, though…)
macro_rules! Into {(
    $( #[$attr:meta] )*
    $pub:vis
    struct $NewType:ident (
        $(#[$field_attr:meta])*
        $field_pub:vis
        $Inner:ty $(,

        $($rest:tt)* )?
    );
) => (
    impl ::core::convert::Into<$Inner> for $NewType {
        #[inline]
        fn into (self: $NewType)
          -> $Inner
        {
            self.0
        }
    }
)} use Into;

macro_rules! From {(
    $( #[$attr:meta] )*
    $pub:vis
    struct $NewType:ident (
        $(#[$field_attr:meta])*
        $field_pub:vis
        $Inner:ty $(,

        $(#[$other_field_attr:meta])*
        $other_field_pub:vis
        $Rest:ty )* $(,)?
    );
) => (
    impl ::core::convert::From<$Inner> for $NewType {
        #[inline]
        fn from (inner: $Inner)
          -> Self
        {
            Self(inner, $($Rest::default),*)
        }
    }
)} use From;
#
# fn main() {}

有一个proc-macro依赖的-lite版本,因此需要不自然的macro_rules!

假设你正在编写一个(普遍且)小巧的依赖项,位于async生态系统内。

  • 由于与async一起工作,你很可能会需要处理pin-projections,因此也会处理::pin-project

  • 但由于其(普遍且)小巧,你不想依赖那些更高级proc-macro crate的重量级三重奏——quote、proc_macro2和syn。

[^only_full_syn_is_heavy]:(注意,只有带有“full”功能的syn才是真正的重量级)

因此,你可能需要寻找像::pin-project-lite这样的东西,以及它的基于pin_project!的polyfill,该polyfill为前者的#[pin_project]属性。

但是,这突然阻碍了你的类型定义的易用性,更糟糕的是,当模式需要为其他功能重复时,它将不会是可组合的(例如,一个类似cell_project!的宏)。

不再多言!是时候使用我们的巧妙技巧了

#[macro_use]
extern crate macro_rules_attribute;

use {
    ::core::pin::{
        Pin,
    },
    ::pin_project_lite::{
        pin_project,
    },
};

#[apply(pin_project!)]
struct Struct<T, U> {
    #[pin]
    pinned: T,
    unpinned: U,
}

impl<T, U> Struct<T, U> {
    fn method(self: Pin<&mut Self>) {
        let this = self.project();
        let _: Pin<&mut T> = this.pinned; // Pinned reference to the field
        let _: &mut U = this.unpinned; // Normal reference to the field
    }
}
#
# fn main() {}

更易用的lazy_static!

假设你有这样一些内容

# use Sync as Logic;
#
static MY_GLOBAL: &dyn Logic = &Vec::<i32>::new();

现在你想改变MY_GLOBAL的值,使其不再是const-可构造的,同时希望最小化这种改变所带来的混乱。

// (For those unaware of it, leaking memory to initialize a lazy static is
// a completely fine pattern, since it only occurs once, and thus, a bounded
// amount of times).
static MY_GLOBAL: &dyn Logic = Box::leak(Box::new(vec![42, 27])); // Error: not `const`!

你可以直接使用一个lazy_static!或一个OnceCell,但这样一来,你的static定义现在比它需要的更嘈杂。是时候给属性位置进行打磨了!

首先,围绕OnceCellLazy类型定义一个辅助函数

macro_rules! lazy_init {(
    $( #[$attrs:meta] )*
    $pub:vis
    static $NAME:ident: $Ty:ty = $init_value:expr ;
) => (
    $( #[$attrs] )*
    $pub
    static $NAME : ::once_cell::sync::Lazy<$Ty> =
        ::once_cell::sync::Lazy::new(|| $init_value)
    ;
)} pub(in crate) use lazy_init;

现在到了使用它的时候了!

# use Sync as Logic;
#
#[macro_use]
extern crate macro_rules_attribute;

#[apply(lazy_init)]
static MY_GLOBAL: &dyn Logic = Box::leak(Box::new(vec![42, 27]));
#
# macro_rules! lazy_init {(
#     $( #[$attrs:meta] )*
#     $pub:vis
#     static $NAME:ident : $Ty:ty = $init_value:expr ;
# ) => (
#     $( #[$attrs] )*
#     $pub
#     static $NAME : ::once_cell::sync::Lazy<$Ty> =
#         ::once_cell::sync::Lazy::new(|| $init_value)
#     ;
# )} use lazy_init;
#
# fn main() {}

调试

一个可选的编译特性"verbose-expansions"可以在编译时打印出此crate中每个宏调用的确切输出

[dependencies]
macro_rules_attribute.version = "..."
macro_rules_attribute.features = ["verbose-expansions"]

特性

derive 别名

# fn main() {}
#[macro_use]
extern crate macro_rules_attribute;

derive_alias! {
    #[derive(Ord!)] = #[derive(PartialEq, Eq, PartialOrd, Ord)];
}

#[derive(Debug, Clone, Copy, Ord!)]
struct Foo {
    //
}

cfg 别名

点击查看
# fn main() {}
#[macro_use]
extern crate macro_rules_attribute;

attribute_alias! {
    #[apply(complex_cfg!)] = #[cfg(
        any(
            any(
                foo,
                feature = "bar",
            ),
            all(
                target_os = "fenestrations",
                not(target_arch = "Pear"),
            ),
        ),
    )];
}

#[apply(complex_cfg!)]
mod some_item { /**/ }

不使用 #[macro_use] extern crate macro_rules_attribute

点击查看

如果您不喜欢 #[macro_use] 无作用域/全局预言语义,那么您可能不喜欢这样做

#[macro_use]
extern crate macro_rules_attribute;
# fn main() {}

像这个文档无处不在地做的那样。

在这种情况下,请记住,您可能确实需要坚持使用 use 导入

use ::macro_rules_attribute::{derive, derive_alias, /**/};
// or even
use ::macro_rules_attribute::*;

derive_alias! {
    #[derive(Copy!)] = #[derive(Clone, Copy)];
}

#[derive(Copy!)]
struct Foo;

甚至在内联完全限定的路径(但请注意,…_alias! 宏在定义内仍然使用无限定路径)

::macro_rules_attribute::derive_alias! {
    #[derive(Copy!)] = #[derive(Clone, Copy)];
}

#[::macro_rules_attribute::derive(Copy!)]
struct Foo;

我个人认为这些方法太嘈杂,不值得这样做,尽管这样获得的“命名空间纯净度”,因此我在其他示例中没有使用该模式。


lib.rs:

不要直接使用此 crate。相反,使用 ::macro_rules_attribute

无运行时依赖

特性