简体   繁体   English

传递给宏规则的“结构字段语法”中的匹配生命周期

[英]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 |         }
 ...

Solution #1 - the cheap solution without capturing lifetimes解决方案 #1 - 不捕获生命周期的廉价解决方案

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"}

Solution #2 - capturing lifetimes解决方案 #2 - 捕获生命周期

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM