宏可以扩展为模式的组合吗?

rvi*_*dal 6 macros pattern-matching rust

从Rust 1.0开始,无法将多个模式分组到一个绑定中:

// It does not compile
match x as char {
    b @ ('A' | 'Z') => println!("A or Z: {}", b),
    _ => println!("Try again")
}

// Correct version
match x as char {
    b @ 'A' | b @ 'Z' => println!("A or Z: {}", b),
    _ => println!("Try again")
}
Run Code Online (Sandbox Code Playgroud)

我想知道宏是否可以完成绑定所有不同可能性的肮脏工作.以下是部分尝试:

macro_rules! bind_alternatives {
    ($x:ident, $e:expr) => ($x @ $e);
    ($x:ident, $e1:expr, $e2:expr) => (
        $x @ $e1 | $x @ $e2
    );
}

fn main() {
    let x = 'a';

    match x {
        bind_alternatives!(z, 'a', 'c') => println!("Matched"),
        _ => println!("No luck")
    };
}
Run Code Online (Sandbox Code Playgroud)

这不编译:

example.rs:4:18: 4:19 error: macro expansion ignores token `|` and any following
example.rs:4         $x @ $e1 | $x @ $e2
                                      ^
example.rs:12:9: 12:40 note: caused by the macro expansion here; the usage of `bind_alternatives` is likely invalid in this context
example.rs:12         bind_alternatives!(z, 'a', 'c') => println!("Matched"),
Run Code Online (Sandbox Code Playgroud)

我知道宏可以扩展为模式,第一个支撑bind_alternatives确实有效.有可能推广到超过1种可能性吗?如果没有,是什么阻止了它?

Chr*_*gan 5

宏可以扩展到模式、表达式和项目等内容,但不是所有内容;具体来说,宏扩展为完整的 AST 节点,但您在这里处理的不是完整的 AST 节点。

match表达式的每个分支都可以有一个或多个模式,由管道分隔,以及一个可选的模式保护 ( )。这是所有特殊的特定语法,因此不是完整的 AST 节点,因此不是宏可以扩展的内容。if conditionmatch

解析器正在其解析的那个点寻找模式,因此它将宏扩展为模式,该模式在 之前结束|,不适合模式语法。因此,宏产生的量超过了可以消耗的量,其余的会因错误而被丢弃。


Rya*_*729 1

我不确定这是什么时候改变的,但最初的帖子中的示例代码现在可以在 Rust 上编译1.63.0

macro_rules! bind_alternatives {
    ($x:ident, $e:expr) => ($x @ $e);
    ($x:ident, $e1:expr, $e2:expr) => (
        $x @ $e1 | $x @ $e2
    );
}

fn main() {
    let x = 'a';

    match x {
        bind_alternatives!(z, 'a', 'c') => println!("Matched"),
        _ => println!("No luck")
    };
    
    foo(b'A')
}

fn foo(x: u8) {
    // It DOES compile
    match x as char {
        b @ ('A' | 'Z') => println!("A or Z: {}", b),
        _ => println!("Try again")
    }
    
    // Correct version
    match x as char {
        b @ 'A' | b @ 'Z' => println!("A or Z: {}", b),
        _ => println!("Try again")
    }
}
Run Code Online (Sandbox Code Playgroud)

游乐场链接