是否可以将参数匹配到Rust宏?

问题描述 投票:0回答:1

请考虑以下代码:

trait Trait {
    fn x(&self) -> u32;
}

struct A {}
impl Trait for A {
    fn x(&self) -> u32 {
        10
    }
}

struct B {}
impl Trait for B {
    fn x(&self) -> u32 {
        20
    }
}

struct C {
    created_time: u64,
}

impl Trait for C {
    fn x(&self) -> u32 {
        30
    }
}

impl C {
    pub fn new() -> C {
        C { created_time: 1000 } // for simplicity
    }
}

macro_rules! create {
    ($type:ident) => {
        match stringify!($type) {
            "C" => Box::new(C::new()) as Box<dyn Trait>,
            _ => Box::new($type {}) as Box<dyn Trait>,
        }
    };
}

fn main() {
    let a: Box<dyn Trait> = create!(A);
    let b: Box<dyn Trait> = create!(B);
    let c: Box<dyn Trait> = create!(C);

    assert_eq!(a.x(), 10);
    assert_eq!(b.x(), 20);
    assert_eq!(c.x(), 30);
}

如果您要求编译器扩展宏,则解析为:

let a: Box<dyn T> =
    match "A" {
        "C" => Box::new(C::new()) as Box<dyn T>,
        _ => Box::new(A{}) as Box<dyn T>,
    };
let b: Box<dyn T> =
    match "B" {
        "C" => Box::new(C::new()) as Box<dyn T>,
        _ => Box::new(B{}) as Box<dyn T>,
    };
let c: Box<dyn T> =
    match "C" {
        "C" => Box::new(C::new()) as Box<dyn T>,
        _ => Box::new(C{}) as Box<dyn T>,
    };

这很好地说明了为什么编译器在尝试编译时会出现以下错误:

error[E0063]: missing field `created_time` in initializer of `C`
  --> mwe.rs:29:27
   |
29 |             _ => Box::new($type { }) as Box<dyn T>,
   |                           ^^^^^ missing `created_time`
...
37 |     let c: Box<dyn T> = create!(C);
   |                         ---------- in this macro invocation

error: aborting due to previous error

但是,我曾希望编译器注意到match "C" { "C" => ..., _ => ... }情况,并删除第二个子句,因为它无论如何都无法运行。遗憾的是,它没有,而是抱怨第二(不可能)子句无法编译。

我还尝试如下用宏中的match替换if,但无济于事:

macro_rules! create {
    ($type:ident) => {
        if stringify!($type) == "C" {
            Box::new(C::new()) as Box<dyn T>
        } else {
            Box::new($type { }) as Box<dyn T>
        }
    }
}

导致

let c: Box<dyn T> =
    if "C" == "C" { Box::new(C::new()) as Box<dyn T> }
    else { Box::new(C{}) as Box<dyn T> };

match尝试相同的错误。

希望Haskell的保护管道语法可以在Rust中工作,我终于也尝试了以下方法:

macro_rules! create {
    ($type:ident) | (stringify!($type) == "C") => {
        Box::new(C::new()) as Box<dyn T>
    },
    ($type:ident) | (stringify!($type) != "C") => {
        Box::new($type { }) as Box<dyn T>
    },
}

但是给了error: no rules expected the token '|'


最终使我回到标题中的问题:

有没有一种方法可以在宏规则中添加“防护”,以告知编译器“如果传递了此参数,则仅运行A,否则对其他对象运行B”?

rust macros match guard
1个回答
0
投票
© www.soinside.com 2019 - 2024. All rights reserved.