[英]Match lifetimes in "struct field syntax" passed to macro_rules
I have defined the following macro because I need many slightly different structs with Serde serialization.我定义了以下宏,因为我需要许多带有 Serde 序列化的稍微不同的结构。
macro_rules! instantiate {
(
$(#[$struct_meta:meta])*
$struct_name:ident {
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_type:ty = $field_value:expr,
)+
}
) => {{
$(#[$struct_meta])*
struct $struct_name {
$(
$(#[$field_meta])*
$field_name: $field_type,
)*
}
$struct_name {
$($field_name: $field_value,)*
}
}};
}
And it should be used just like this:它应该像这样使用:
instantiate! {
#[derive(serde::Serialize)]
RequestParams {
#[serde(serialize_with="serialize_debug")]
ids: Vec<Base62Uint> = version_ids
.into_iter()
.map(std::fmt::Display::to_string)
.collect(),
}
}
However, I have another use for it:但是,我还有另一个用途:
let (hash, kind) = match hash {
FileHashes {
sha512: Some(hash), ..
} => (hash, "sha512"),
FileHashes {
sha1: Some(hash), ..
} => (hash, "sha1"),
_ => todo!(),
};
instantiate! {
#[derive(serde::Serialize)]
RequestParams {
algorithm: &str = kind,
}
}
I end up with the following error, which was expected:我最终得到以下错误,这是预期的:
error[E0106]: missing lifetime specifier
--> src/endpoints.rs:236:28
|
236 | algorithm: &str = kind,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
88 ~ struct $struct_name<'a> {
89 | $(
90 | $(#[$field_meta])*
91 | $field_name: $field_type,
92 | )*
93 | }
...
I want this macro to create a new lifetime for every $field_name
, where the corresponding $field_type
is a reference.我希望这个宏为每个$field_name
创建一个新的生命周期,其中相应的$field_type
是一个引用。
Here is what I have attempted:这是我尝试过的:
$field_name: $(&$field_life:lifetime)? $field_type,
And stopped because I realized that would only capture the input lifetimes, which is less ergonomic than what I want, and I would end up with an ambiguity in the pattern because it input would be able to match either the &$field_life:lifetime
portion or the $field_type:ty
.并停止,因为我意识到这只会捕获输入生命周期,这比我想要的更不符合人体工程学,并且我最终会在模式中出现歧义,因为它输入将能够匹配&$field_life:lifetime
部分或$field_type:ty
。
I assume that your minimal reproducible example is我假设您的最小可重现示例是
macro_rules! instantiate {
(
$(#[$struct_meta:meta])*
$struct_name:ident {
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_type:ty = $field_value:expr,
)+
}
) => {{
$(#[$struct_meta])*
struct $struct_name {
$(
$(#[$field_meta])*
$field_name: $field_type,
)*
}
$struct_name {
$($field_name: $field_value,)*
}
}};
}
fn main() {
let x = instantiate! {
#[derive(serde::Serialize)]
RequestParams {
#[serde(default)]
id: u32 = 10,
}
};
println!("{}", serde_json::to_string(&x).unwrap());
}
{"id":10}
and your second example is, with the macro being identical:你的第二个例子是,宏是相同的:
fn main() {
let (hash, kind) = (1234, "sha512");
let x = instantiate! {
#[derive(serde::Serialize)]
RequestParams {
algorithm: &str = kind,
}
};
println!("{}", serde_json::to_string(&x).unwrap());
}
error[E0106]: missing lifetime specifier
--> src/main.rs:31:24
|
31 | algorithm: &str = kind,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
12 ~ struct $struct_name<'a> {
13 | $(
14 | $(#[$field_meta])*
15 | $field_name: $field_type,
16 | )*
17 | }
...
fn main() {
let (hash, kind) = (1234, "sha512");
let x = instantiate! {
#[derive(serde::Serialize)]
RequestParams {
algorithm: &'static str = kind,
}
};
println!("{}", serde_json::to_string(&x).unwrap());
}
{"algorithm":"sha512"}
macro_rules! instantiate {
(
$(#[$struct_meta:meta])*
$struct_name:ident $(< $($lifetimes:lifetime),* >)? {
$(
$(#[$field_meta:meta])*
$field_name:ident: $field_type:ty = $field_value:expr,
)+
}
) => {{
$(#[$struct_meta])*
struct $struct_name $(< $($lifetimes),* >)? {
$(
$(#[$field_meta])*
$field_name: $field_type,
)*
}
$struct_name {
$($field_name: $field_value,)*
}
}};
}
fn main() {
let (hash, kind) = (1234, "sha512".to_string());
let x = instantiate! {
#[derive(serde::Serialize)]
RequestParams<'a> {
algorithm: &'a str = &kind,
}
};
println!("{}", serde_json::to_string(&x).unwrap());
}
{"algorithm":"sha512"}
macro_rules!
cannot generate lifetimes, for the same reasons they cannot generate identifiers ( Can a Rust macro create new identifiers? ), because they cannot (without help) generate new token.不能生成生命周期,因为它们不能生成标识符( Rust 宏可以创建新的标识符吗? ),因为它们不能(没有帮助)生成新的令牌。 You have to use a procedural macro for that.您必须为此使用程序宏。
You can use existing procedural macros that allow you to generate tokens, for example @dtolnay's seq-macro
crate, but it will raise another problem: how do you know where to put the lifetime?您可以使用允许您生成令牌的现有过程宏,例如@dtolnay 的seq-macro
crate,但它会引发另一个问题:您如何知道将生命周期放在哪里? You can (partially, because you cannot identify all types requiring lifetimes) solve that by tt-munching and identifying types that require lifetimes (for example references), but this will make your macro really complex, even more than a proc macro.您可以(部分是因为您无法识别所有需要生命周期的类型)通过 tt-munch 和识别需要生命周期的类型(例如引用)来解决这个问题,但这会使您的宏非常复杂,甚至比 proc 宏还要复杂。 I do not recommend doing that.我不建议这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.