rust - 具有三个可选参数的宏,其中一个必须接受值列表

标签 rust rust-macros

我有一个带有两个可选字段的结构,并想提供特殊的宏来简化实例的创建。

宏必须接受一个必需的参数和三个可选的参数。最后一个可选参数必须接受值列表。我想为此使用一种模式。

最后,我做到了,一切正常:

#[derive(Debug)]
pub enum Variant {
    OptionOne,
    OptionTwo,
}

#[derive(Debug)]
pub struct TheStruct{
    string: String,
    first: Option<Variant>,
    second: Option<Variant>,
    numbers: Vec<u32>,
}

impl TheStruct {
    // doesn't matter
}

#[doc(hidden)]
#[macro_export]
macro_rules! the_macro_int {
    ($the_struct:ident, numbers, { $($w:expr),*$(,)*}) => {
        $($the_struct.numbers.push($w);)*
    };
    ($the_struct:ident, first, {$w:expr}) => {
        $the_struct.first = Some($w);
    };
    ($the_struct:ident, second, {$w:expr}) => {
        $the_struct.second = Some($w);
    };
}

#[macro_export]
macro_rules! the_macro {
    (string: $string:expr, $($kind:ident : $val:tt),*$(,)*) => {{
        let mut the_struct = $crate::TheStruct{
            string: $string,
            first: None,
            second: None,
            numbers: std::vec::Vec::new(),
        };
        $($crate::the_macro_int!(the_struct, $kind, $val);)*
        the_struct
    }};
}

fn main() {
    let the_struct = the_macro!(
        string: "Hello".to_owned(),
        first: { Variant::OptionOne },
        numbers: (1, 3, 4),
    );
    println!("the_struct:{:?}", the_struct);
}

Playground

一件事使我不高兴:first: { Variant::OptionOne },中的括号。

我试图用(string: $string:expr, $($kind:ident : $val:expr),*$(,)*)替换我的模式,但是它不再适用于numebrs

是否可以重新定义the_macro的模式,以使first: Variant::OptionOne,有效,而numbers仍可以接受项目列表? numbers的括号并不重要,但是我不能仅用Vec或类似内容替换列表。

P.S.我不是在寻找具有多种模式的解决方案。

最佳答案

您的宏有两个问题:

  • 首先,它尝试执行太多操作。为什么在数组文字使用numbers/numbers: (1, 3, 4)的情况下使用[初始化],而您只能用4个额外的字符来初始化矢量?

    让我们使用更自然的表示法numbers: vec![1, 3, 4]。这具有使您的用户也可以使用现有矢量初始化字段的优点。
  • 其次,您需要在{ Variant::OptionOne }周围使用方括号,因为您使用tt作为值,但是Variant::OptionOne并不是一种tt,而是3。如果将模式更改为更自然的expr,则可以直接使用Variant::OptionOne:

    (string: $string:expr, $($kind:ident : $val:expr),*$(,)*) => {{
    

  • (Permalink to the playground)

    但是,我建议您不要使用这样的宏。在Rust中处理此类结构初始化的常用方法是builder pattern

    关于rust - 具有三个可选参数的宏,其中一个必须接受值列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61261661/

    相关文章:

    generics - 使用宏编写 const 泛型枚举组合

    generics - 指定泛型参数属于一小组类型

    rust - 如何通过 Rust 宏将表达式中的一个标识符替换为另一个标识符?

    rust - 如何使用 Serde 在反序列化期间转换字段?

    asynchronous - 在实践中实现Future时如何使用Context和Wakers

    rust - 我们可以创建自定义 Rust 运算符吗?

    multithreading - 多线程应用程序无法使用错误链进行编译

    rust - 使用rust 或两个选项之间

    rust - 如何区分macro_rules宏中的不同种类的项目?

    macros - 如何在宏中匹配 Rust 的 `if` 表达式?