Declarative marco isn't getting recursively invoked with repeated parameters

huangapple go评论67阅读模式
英文:

Declarative marco isn't getting recursively invoked with repeated parameters

问题

I can help you with the translation of the provided text. Here is the translated version:

我想要编写一个宏,它生成一个带有默认的 fn new(...) 函数的结构体。如果所需的结构体具有 String 字段,我希望 new 函数是一个模板函数,类似于 fn new<S: Into<String>>(..., field: S, ...)

问题出现在创建这些可选的模板参数时。

例如,对于以下 Rust 代码块:

impl_type!(
    User {
        id: i64,
        name: String,
    }
)

我想要获得以下结果:

pub struct User { /* 字段 */ }

impl User {
    pub fn new<S: Into<String>>(id: i64, name: S) -> Self {
        User {
            id: id,
            name: name.into(),
        }
    }
}

但我得到的是:

error: 期望 `(`  `&lt;`,找到 `impl_type`
  --> src/macros/impl_type.rs:39:24
   |
38 |           impl $name {
   |  ____________________-
   | |____________________|
   | |
39 | |             pub fn new impl_type!(@template_if_string $($field_type,)*) -> Self {
   | |                        ^^^^^^^^^ 期望 `(`  `&lt;`
40 | |             ($($field: impl_type!(@ty_fn $field_type),)*) -> Self {

我的 macro_rules! 代码如下:

macro_rules! impl_type {
    (
        $name:ident {
            $($field:ident : $field_type:ty,)*
        }
    ) => {
        pub struct $name {
            $($field: $field_type,)*
        }

        impl $name {
            pub fn new<$($field: impl_type!(@ty_fn $field_type),)*>($($field: $field_type,)*) -> Self {
                $name {
                    $($field: ($field).into(),)* 
                }
            }
        }
    };

    (@ty_fn String) => ( S );
    (@ty_fn $field_type:ty) => ( impl_type!($field_type) );

    (@template_if_string) => {};
    (@template_if_string String, $($field_type:ty,)*) => {
        <S: std::convert::Into<String>>
    };
    (@template_if_string $field_type:ty, $($other_field_type:ty,)*) => {
        impl_type!(@template_if_string $($other_field_type,)*)
    };
}

如何在 new 后调用 impl_type! 或重构代码以使其按预期工作?

我是 Rust 新手。感谢您的耐心,任何帮助都将不胜感激。

英文:

I want to write a macro, which generates a struct with a default fn new(...) function. If the desired struct has a String field, I want new to be a template function like fn new&lt;S: Into&lt;String&gt;&gt;(..., field: S,...).

The problem occurred when creating these optional template parameter.


For example, for

impl_type!(
    User {
        id: i64,
        name: String,
    }
}

I want to get:

pub struct User { /* fields */ }

impl User {
    pub fn new&lt;S: Into&lt;String&gt;&gt;(id: i64, name: S) -&gt; Self {
        User {
            id: id,
            name: name.into(),
        }
    }
}

What I'm getting:

error: expected one of `(` or `&lt;`, found `impl_type`
  --&gt; src/macros/impl_type.rs:39:24
   |
38 |           impl $name {
   |  ____________________-
   | |____________________|
   | |
39 | |             pub fn new impl_type!(@template_if_string $($field_type,)*)
   | |                        ^^^^^^^^^ expected one of `(` or `&lt;`
40 | |             ($($field: impl_type!(@ty_fn $field_type),)*) -&gt; Self {

My macro_rules! code:

macro_rules! impl_type {
    (
        $name:ident {
            $($field:ident : $field_type:ty,)*
        }
    ) =&gt; {
        pub struct $name {
            $($field: $field_type,)*
        }

        impl $name {
            pub fn new impl_type!(@template_if_string $($field_type,)*)
            ($($field: impl_type!(@ty_fn $field_type),)*) -&gt; Self {
                $name {
                    $($field: ($field).into(),)* 
                }
            }
        }
    };

    (@ty_fn String) =&gt; ( S );
    (@ty_fn $field_type:ty) =&gt; ( impl_type!($field_type) );

    (@template_if_string) =&gt; {};
    (@template_if_string String, $($field_type:ty,)*) =&gt; {
        &lt;S: std::convert::Into&lt;String&gt;&gt;
    };
    (@template_if_string $field_type:ty, $($other_field_type:ty,)*) =&gt; {
        impl_type!(@template_if_string $($other_field_type,)*)
    };
}

How could I invoke impl_type! after new or refactor the code so it works as intended?

I'm new to Rust. Any help is appreciated, thanks for your patience.

答案1

得分: 1

你正在尝试的操作无法使用声明性宏来完成,因为每个声明性宏必须返回语法树的有效节点。例如,你不能仅仅在声明性宏中插入{标记,而是必须返回有效的语法树节点。

类似地,仅具有带括号但不带名称的函数声明参数不是语法树的节点。我甚至不确定是否可能使用声明性宏来实现你想要的操作,但无论如何,这更适合于过程性宏:

  • 过程性宏在使用的代码中具有更轻量级的语法(#[something]...而不是something! { ... });
  • 过程性宏更广泛,即使你设法使用声明性宏来实现你想要的操作,也很可能难以支持泛型;
  • 过程性宏让你可以访问已经实现解析和遍历标准结构定义的库。
英文:

What you are trying to do doesn't work with declarative macros, because each declarative macro must return a valid node of the syntax tree. For instance, you couldn't have a declarative macro just insert the token { where it was invoked.

Similarly, just the arguments of a function declaration, with the parenthesis but without the name, is not a node of the syntax tree. I'm not even sure if it's possible to achieve what you are trying to do with declarative macros, but in any case it's a problem much better suited for procedural macros:

  • procedural macros have a more lightweight syntax in the used code (#[something]... instead of something! { ... });
  • procedural macros are much broader, that is, even if you achieved to do what you are trying to do with declarative macros, it would most likely be a nightmare to also support generics;
  • procedural macros given you access to libraries that already implement parsing and traversing standard struct definitions.

huangapple
  • 本文由 发表于 2023年5月6日 22:02:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76189307.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定