标签: rust-proc-macros

如何创建 proc_macro_attribute?

既然proc_macros 已经稳定了,怎么造出这样的东西?

据我所知,可以选择在 a 上添加#[proc_macro_attribute]注释fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream,但我如何注册它?如何添加自定义属性?

rust rust-macros rust-proc-macros

4
推荐指数
1
解决办法
5738
查看次数

是否可以在Rust的程序宏中存储状态?

是否可以构建一个不输出任何东西,而是存储状态以建立列表的宏,然后再构建一个实际使用该数据的宏?

例如:

trait SomeTrait {}

#[derive(mark)]
struct Person {}

impl SomeTrait for Person {}

#[derive(mark)]
struct Item {}

impl SomeTrait for Item  {}

#[derive(mark)]
struct Object {}

impl SomeTrait for Object {}

create_mapper! // this then outputs the below function
//assuming for the fact that data is loaded correctly before this macro is used

fn select_item(kind: String) -> impl SomeTrait {
    match kind {
        "person" => Person,
        "item" => Item,
        "object" => Object,        
    }
}
Run Code Online (Sandbox Code Playgroud)

rust rust-macros rust-proc-macros

4
推荐指数
1
解决办法
370
查看次数

proc宏可以确定调用编译的目标吗?

过程宏存在于它们自己的 crate 中,这些 crate 是为开发机器编译的(以便在编译使用它们的 crate 时可以执行它们)。过程宏包中的任何条件编译指令将相应地基于编译环境,而不是调用包的编译环境。

\n\n

当然,此类宏可以扩展为包含条件编译指令的标记,然后将在调用包的编译\xe2\x80\x94的上下文中对这些指令进行评估,但是,这并不总是可能或理想的。

\n\n

如果希望扩展令牌本身成为调用包的编译环境的某个函数,则需要宏在其运行时确定该环境(当然是调用包的编译) -时间)。显然是该模块的完美用例std::env

\n\n

然而,rustc 不设置任何环境变量;而货物套装数量有限。特别是,一些关键信息(如目标架构/操作系统等)根本不存在。

\n\n

我很欣赏调用包中的构建脚本可以为宏设置环境变量然后读取,但这给调用包作者带来了令人不满意的负担。

\n\n

有没有什么方法可以让 proc 宏作者获得有关调用包的编译环境的运行时信息(我最感兴趣的目标体系结构和操作系统)?

\n

macros compilation environment-variables rust rust-proc-macros

4
推荐指数
1
解决办法
975
查看次数

推断调用包的名称以填充程序宏中的文档测试

我正在创建一个程序宏,它从某些配置文件自动生成一个库(它是一个寄存器布局,但这对于问题来说并不重要)。

我希望该库自动生成自动库随附的文档,并包含应与cargo test. 现在,我已经实现了其中大部分内容,但有一个问题我找不到解决方案。

假设我们有一个名为的库my_lib,我们在其中调用宏来填充它:

use my_macro_lib::hello;

hello!();
Run Code Online (Sandbox Code Playgroud)

它扩展到类似:

/// `foo` will always return `true`
/// ```
/// use my_lib;
/// assert!(my_lib::foo());
/// ```
pub fn foo() -> bool {
    true
}
Run Code Online (Sandbox Code Playgroud)

这将按预期运行 -cargo doc将做正确的事情并将cargo test按预期运行文档测试。

问题是,在这个例子中,use my_lib被硬编码到这my_macro_lib显然是不可取的。

如何创建一个宏来推断正在进行调用的板条箱的名称?

我尝试使用macro_rules!程序宏内部来扩展$crate,但这违反了卫生规则。

doctest rust rust-macros rust-proc-macros

4
推荐指数
1
解决办法
1654
查看次数

如何将标识符 (`proc_macro::Ident`) 存储为常量以避免重复?

我正在编写一个程序宏,我需要多次发出一个很长的标识符(可能是因为卫生,例如)。我quote!用来创建TokenStreams,但我不想一遍又一遍地重复长标识符!

例如,我想生成此代码:

let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
Run Code Online (Sandbox Code Playgroud)

我知道我可以创建一个Ident并将其插入到quote!

let my_ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
quote! {
    let #my_ident = 3;
    println!("{}", #my_ident);
    println!("twice: {}", #my_ident + #my_ident);
}
Run Code Online (Sandbox Code Playgroud)

到目前为止一切顺利,但我需要在我的代码库中的许多函数中使用该标识符。我希望它是const我可以在任何地方使用的。但是,这失败了:

const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
Run Code Online (Sandbox Code Playgroud)

出现此错误:

let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
Run Code Online (Sandbox Code Playgroud)

我怀疑这些功能是否会const很快被标记。

我可以使字符串本身成为一个常量:

const IDENT: &str = "very_long_ident_is_very_long_indeed";
Run Code Online (Sandbox Code Playgroud)

但是无论我想在哪里使用标识符,我都需要调用Ident::new(IDENT, Span::call_site()),这会很烦人。我只想写#IDENT在我的quote! …

rust rust-proc-macros

4
推荐指数
1
解决办法
308
查看次数

Rust proc_macro_derive:如何检查字段是否属于基本类型,例如布尔值?

我正在尝试过滤掉结构体中类型为 的所有字段bool。但syn::Type枚举似乎没有案例,或者我错误地阅读了定义:

pub enum Type {
    Array(TypeArray),
    BareFn(TypeBareFn),
    Group(TypeGroup),
    ImplTrait(TypeImplTrait),
    Infer(TypeInfer),
    Macro(TypeMacro),
    Never(TypeNever),
    Paren(TypeParen),
    Path(TypePath),
    Ptr(TypePtr),
    Reference(TypeReference),
    Slice(TypeSlice),
    TraitObject(TypeTraitObject),
    Tuple(TypeTuple),
    Verbatim(TokenStream),
    // some variants omitted
}
Run Code Online (Sandbox Code Playgroud)

我查看了源syn::Types代码,以检查省略了哪些变体,但这并没有给我带来任何进一步的帮助。这是我到目前为止所拥有的:

#[proc_macro_derive(Creator)]
pub fn derive_creator(_item: TokenStream) -> TokenStream {
    let item = parse_macro_input!(_item as syn::DeriveInput);
    let item_ident = item.ident;

    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = item.data
    {
        named
    } else {
        panic!("You can derive Creator only on …
Run Code Online (Sandbox Code Playgroud)

rust rust-proc-macros

4
推荐指数
1
解决办法
1936
查看次数

如何用亲爱的嵌套参数解析属性?

我正在尝试使用darling解析属性,并且我想支持以下用法:

// att not specified
#[derive(MyTrait)]
struct Foo(u64);

// att specified without an argument
#[derive(MyTrait)]
#[myderive(att)]
struct Foo(u64);

// att specified with an argument
#[derive(MyTrait)]
#[myderive(att(value = "String"))]
struct Foo(u64);
Run Code Online (Sandbox Code Playgroud)

这些是我的类型:

#[derive(FromDeriveInput)]
#[darling(attributes(myderive))]
struct MyDeriveInput {
    #[darling(default)]
    att: Option<MyAttr>,
}

#[derive(FromMeta, Default)]
struct MyAttr {
    #[darling(default)]
    value: Option<Path>,
}
Run Code Online (Sandbox Code Playgroud)

还有一个测试:

#[test]
fn test() {
    let derive_input = syn::parse_str(
        r#"
        #[derive(MyTrait)]
        #[myderive(att)]
        struct Foo(u64);
    "#,
    )
    .unwrap();

    let parsed: MyDeriveInput = FromDeriveInput::from_derive_input(&derive_input).unwrap();
    assert!(parsed.att.is_some());
}
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

// att not specified
#[derive(MyTrait)] …
Run Code Online (Sandbox Code Playgroud)

rust rust-proc-macros

4
推荐指数
1
解决办法
1491
查看次数

使用 Rust proc 宏生成动态命名的结构体实例方法

我正在尝试编写一个过程宏,它生成将所有字段加倍的方法f64。我让它在 ./src/main.rs 的单个字段中工作

use attr_macro::DoubleF64;

#[derive(DoubleF64)]
struct MyStruct {
    my_string: String,
    my_number: f64,
    my_other_number: f64,
}


fn main() {
    let mystruct = MyStruct {
        my_string: "some str".to_string(),
        my_number: 2.0,
        my_other_number: 2.0,
    };
    println!("my_number * 2: {}", mystruct.double_my_number());
}

Run Code Online (Sandbox Code Playgroud)

和./proc_macro/src/lib.rs:

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput, FieldsNamed};

#[proc_macro_derive(DoubleF64)]
pub fn double_f64(input: TokenStream) -> TokenStream {
    let DeriveInput { ident, data, .. } = parse_macro_input!(input);

    let (func_name, fident) = if let syn::Data::Struct(s) = data { …
Run Code Online (Sandbox Code Playgroud)

macros rust rust-proc-macros

4
推荐指数
1
解决办法
2859
查看次数

错误:当使用自定义 proc_macro 以及用 Rust 中的 darling 编写的属性时,“无法在此范围内找到属性”

我正在编写一个库,其中包含具有自定义属性的自定义派生宏。为此,我使用darling. 因此,我的项目结构如下:

\n
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pg-worm\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pg-worm-derive\n\xe2\x94\x82   \xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src/lib.rs\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src/lib.rs\n
Run Code Online (Sandbox Code Playgroud)\n

我的 proc 宏是这样指定的 ( pg-worm/pg-worm-derive/src/lib.rs):

\n
use darling::FromDeriveInput;\nuse proc_macro::{self, TokenStream};\nuse syn::parse_macro_input;\n\n#[derive(FromDeriveInput)]\n#[darling(attributes(table))]\nModelInput {\n    table_name: Option<String>\n}\n\n#[proc_macro_derive(Model)]\npub fn derive(input: TokenStream) -> TokenStream {\n    let opts = ModelInput::from_derive_input(&parse_macro_input!(input)).unwrap();\n\n    // macro magic happens\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后我从主文件 ( pg-worm/src/lib.rs) 中重新导出宏:

\n
pub use pg_worm_derive::*;\n\npub trait Model {\n    // trait specs\n}\n
Run Code Online (Sandbox Code Playgroud)\n

但是当我使用以下代码测试我的宏时(也在pg-worm/src/lib.rs):

\n
#[cfg(test)]\nmod tests {\n    use pg_worm::Model;\n\n    #[derive(Model)]\n    #[table(table_name = "person")]\n    struct Person {\n        id: i64,\n        name: …
Run Code Online (Sandbox Code Playgroud)

rust rust-proc-macros rust-attributes

4
推荐指数
1
解决办法
1220
查看次数

为什么 serde 的 Serialize/Deserialize 派生会在 const _: () 块中生成代码?

注意:这个问题并不是专门关于serde:它是关于过程宏和 const 函数。

我正在研究serde的派生宏为Serialize和生成的代码Deserialize,但我还没有弄清楚为什么它的所有代码都在const _: ().

例如,

#[derive(Deserialize)]
struct MyNewType(i32);
Run Code Online (Sandbox Code Playgroud)

扩展为(使用cargo expand):

#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _: () = {
    #[allow(unused_extern_crates, clippy::useless_attribute)]
    extern crate serde as _serde;
    #[automatically_derived]
    impl<'de> _serde::Deserialize<'de> for MyNewType {
        fn deserialize<__D>(
            __deserializer: __D,
        ) -> _serde::__private::Result<Self, __D::Error>
        where
            __D: _serde::Deserializer<'de>,
        {
         // generated implementation omitted for brevity
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

我发现,如果我仔细复制块内的内容const _: () = {...},并在删除标签后将其粘贴回源代码中#[derive(Deserialize)],那么一切似乎都会编译并且反序列化继续正常工作。const _: () …

compile-time rust rust-proc-macros

4
推荐指数
1
解决办法
55
查看次数