如何在Rust宏中使用ty

时间:2016-05-05 19:31:28

标签: unit-testing macros rust

我正在尝试编写一个通用的解决方案来为单元测试Rust代码提供装置。我想出了一个宏,它允许用户定义 setup teardown 方法。到目前为止,这是我的解决方案:

struct FooTestFixture {
    pub name : String
}

impl FooTestFixture {
    fn setup() -> FooTestFixture {
        FooTestFixture { name: String::from("Initialised") }
    }
}

fn teardown(fixture : &mut FooTestFixture) {
    fixture.name = "".to_string();
}

macro_rules! unit_test {
    ($name:ident $fixt:ident $expr:expr) => (
        #[test]
        fn $name() {
            let mut $fixt : FooTestFixture = FooTestFixture::setup();
            $expr;

            teardown(&mut $fixt);
        }
    )
}

unit_test! (heap_foo_fixture_should_be_initialised_using_macro f {
    assert_eq!(f.name, "Initialised");
});

这很有效。唯一的问题是,宏unit_test不是通用的,并且绑定到夹具名称​​ FooTestFixture 。这意味着每个测试模块都需要为每个测试夹具重新定义这个宏,这是不理想的。我希望能够做的是引入一个类型变量并在宏扩展中使用该类型。深入研究宏我发现有一个'ty'项,代表一种类型,我想我可以做到这一点......

macro_rules! unit_test {
    ($name:ident $fixt:ident $ftype:ty $expr:expr) => (
        #[test]
        fn $name() {
            let mut $fixt : $ftype = $ftype::setup();
            $expr;

            teardown(&mut $fixt);
        }
    )
}

unit_test! (heap_foo_fixture_should_be_initialised_using_macro FooTestFixture f {
    assert_eq!(f.name, "Initialised");
});

但是,这不起作用并导致以下错误:

  

src \ tests \ heap_fixture_with_new.rs:48:40:48:50错误:$ftype:ty是   后跟$expr:exprty片段不允许这样做   src \ tests \ heap_fixture_with_new.rs:48($ name:ident $ fixt:ident   $ ftype:ty $ expr:expr)=> (

正如您所看到的,在宏定义中,我已经用$ ftype替换了对FooTestFixture的引用。

我正在努力实现的目标是什么?这几乎就像我希望宏是通用的,允许你传入一个类型,在宏定义中使用。

2 个答案:

答案 0 :(得分:3)

ty不能直接跟expr。必须是followed by a specific set of tokens

  • =>
  • ,
  • =
  • |
  • ;
  • :
  • >
  • [
  • {
  • as
  • where

exprstmtpathpat之后存在类似的限制。这是在RFC 550中引入future-proof potential change in Rust syntax

要修复它,您需要更改宏的模式,例如

macro_rules! unit_test {
    ($name:ident $fixt:ident<$ftype:ty> $expr:expr) => (
//                          ^         ^ followed by '>' is OK

unit_test! (test_name fixture_name<FooTestFixture> f {    
//                                ^              ^

答案 1 :(得分:2)

我意识到毕竟我并不需要ty。我可以将类型指定为ident参数,以便以下工作:

macro_rules! unit_test {
    ($name:ident $fixt:ident $ftype:ident $expr:expr) => (
        #[test]
        fn $name() {
            let mut $fixt = $ftype::setup();
            $expr;

            teardown(&mut $fixt);
        }
    )
}

unit_test! (foo_fixture_should_be_initialised_using_generic_macro f FooTestFixture {
    assert_eq!(f.name, "Initialised");
});