【问题标题】:How do I create a Rust macro with optional parameters using repetitions?如何使用重复创建带有可选参数的 Rust 宏?
【发布时间】:2015-12-19 16:58:39
【问题描述】:

我目前正在研究 Rust 宏,但找不到任何关于重复的详细文档。我想创建带有可选参数的宏。这是我的想法:

macro_rules! single_opt {
    ($mand_1, $mand_2, $($opt:expr)* ) =>{
        match $opt {
            Some(x) => println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, x);
            None => single_opt!($mand_1, $mand_2, "Default");
        }
    }
}

fn main() {
    single_opt!(4,4);
}

这个example 似乎已经过时了,因为我无法编译它。 Rust 书只是非常简短地提到了这个主题。如何让这个示例工作?

【问题讨论】:

    标签: macros pattern-matching rust


    【解决方案1】:

    注意:由于编写了此答案,Rust has gained the ability to express optional elements in a pattern(在 Rust 1.32.0 中稳定)使用语法 $(tokens)?

    Rust 书的第一版有一个 rather long chapter on macros,但是关于重复的部分在示例上有点害羞......

    有几种方法可以处理宏中的可选参数。如果您有一个只能出现一次的可选参数,那么您不应该使用重复:您应该在宏中定义多个模式,如下所示:

    macro_rules! single_opt {
        ($mand_1:expr, $mand_2:expr) => {
            single_opt!($mand_1, $mand_2, "Default")
        };
        ($mand_1:expr, $mand_2:expr, $opt:expr) => {
            println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $opt)
        };
    }
    
    fn main() {
        single_opt!(4, 4);
    }
    

    如果你想允许任意数量的参数,那么你需要重复。您的原始宏不起作用,因为您将逗号放在重复项之外,因此您必须将宏调用为single_opt!(4,4,);。相关案例见How to allow optional trailing commas in macros?

    如果您有固定数量的参数后跟重复,则可以将逗号作为第一个标记放在重复中:

    macro_rules! single_opt {
        ($mand_1:expr, $mand_2:expr $(, $opt:expr)*) => {
            println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $($opt),*)
        };
    }
    

    但是,在这种特定情况下它不起作用:

    error: 3 positional arguments in format string, but there are 2 arguments
     --> src/main.rs:3:22
      |
    3 |         println!("1. {} 2. {}, 3. {}", $mand_1, $mand_2, $($opt),*)
      |                      ^^    ^^     ^^
    ...
    8 |     single_opt!(4, 4);
      |     ------------------
      |     |
      |     in this macro invocation
      |     in this macro invocation
      |     in this macro invocation
      |
      = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
    

    所以我们必须回到定义两种模式:

    macro_rules! single_opt {
        ($mand_1:expr, $mand_2:expr) => {
            single_opt!($mand_1, $mand_2, "Default")
        };
        ($mand_1:expr, $mand_2:expr, $($opt:expr),*) => {
            {
                println!("1. {} 2. {}", $mand_1, $mand_2);
                $(
                    println!("opt. {}", $opt);
                )*
            }
        };
    }
    
    fn main() {
        single_opt!(4, 4, 1, 2);
    }
    

    重复采用$( PATTERN ) SEPARATOR COUNT 的形式,其中PATTERN 是您要重复的模式,SEPARATOR 是分隔每个重复的可选标记(这里是,),COUNT 是@ 987654335@ 表示“零次或多次出现”,+ 表示“一次或多次出现”。

    然后,在宏扩展中,我们需要一个重复块才能访问$opt。语法完全相同,但请注意分隔符不必相同(此处,扩展中没有分隔符)。

    【讨论】:

      猜你喜欢
      • 2022-08-19
      • 1970-01-01
      • 2015-04-08
      • 2014-09-16
      • 2012-03-21
      • 1970-01-01
      • 2019-11-07
      • 1970-01-01
      相关资源
      最近更新 更多