匹配“结构字段语法”中的寿命传递给macro_rules

发布于 2025-02-06 10:04:26 字数 2315 浏览 1 评论 0原文

我已经定义了以下宏,因为我需要经过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,)*
        }
    }};
}

它应该像这样使用:

instantiate! {
    #[derive(serde::Serialize)]
    RequestParams {
        #[serde(serialize_with="serialize_debug")]
        ids: Vec<Base62Uint> = version_ids
            .into_iter()
            .map(std::fmt::Display::to_string)
            .collect(),
    }
}

但是,我还有另一种用途:

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

我最终遇到了以下错误,这是可以预期的:

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

我希望这个宏为每个$ field_name创建一个新的生命周期,其中相应的$ field_type是参考。

这是我尝试过的:

$field_name: $(&$field_life:lifetime)? $field_type,

停下来,因为我意识到这只会捕获输入寿命,这比我想要的不太符合人体工程学,而我最终会在模式中含糊不清,因为它输入可以匹配任何一个&amp; $ field_life:lifetime部分或$ field_type:ty

I have defined the following macro because I need many slightly different structs with Serde serialization.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

以可爱出名 2025-02-13 10:04:26

我认为您的最低可再现示例是

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}

,您的第二个例子是,宏是相同的:

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

解决方案#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"}

解决方案#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"}

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

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

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"}
萌辣 2025-02-13 10:04:26

macro_rules!无法生成寿命,其原因与他们无法生成标识符的原因相同( Rust Macro可以创建新的标识符?),因为它们无法(没有帮助)生成新的令牌。您必须使用程序宏。

您可以使用现有的程序宏来允许您生成令牌,例如 @dtolnay的 /code> 板条箱,但这会引起另一个问题:您如何知道将一生放在哪里?您可以(部分地,因为您无法识别所有需要生命的类型)来解决这一问题,通过tt-munnering和识别需要生命的类型(例如参考),但这将使您的宏观真正的 Complex比PROC宏。我不建议这样做。

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. 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? 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. I do not recommend doing that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文