英文:
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: 期望 `(` 或 `<`,找到 `impl_type`
--> src/macros/impl_type.rs:39:24
|
38 | impl $name {
| ____________________-
| |____________________|
| |
39 | | pub fn new impl_type!(@template_if_string $($field_type,)*) -> Self {
| | ^^^^^^^^^ 期望 `(` 或 `<`
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<S: Into<String>>(..., 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<S: Into<String>>(id: i64, name: S) -> Self {
User {
id: id,
name: name.into(),
}
}
}
What I'm getting:
error: expected one of `(` or `<`, found `impl_type`
--> 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 `<`
40 | | ($($field: impl_type!(@ty_fn $field_type),)*) -> Self {
My macro_rules!
code:
macro_rules! impl_type {
(
$name:ident {
$($field:ident : $field_type:ty,)*
}
) => {
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),)*) -> 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,)*)
};
}
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 ofsomething! { ... }
); - 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论