2016-05-05 3 views
3

Я пытаюсь составить общее решение для обеспечения светильников для модульного тестирования кода ржавчины. Я придумал макрос, который позволяет пользователю определить setup и teardown методов. Вот мое решение:Как использовать ty в макросе Rust

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. Это означает, что каждому тестовому модулю необходимо переопределить этот макрос для каждого тестового прибора, что не является идеальным. То, что я хотел бы сделать, это также ввести переменную типа и использовать этот тип в расширении макроса. Копаясь больше в макросы я обнаружил, что существует «ти» элемент, который представляет собой тип, и я думал, что я мог бы сделать это ...

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 error: $ftype:ty is followed by $expr:expr , which is not allowed for ty fragments src\tests\heap_fixture_with_new.rs:48 ($name:ident $fixt:ident $ftype:ty $expr:expr) => (

Как вы можете видеть, в определении макроса, я заменил ссылки на FooTestFixture с $ Ftype.

Является ли то, что я пытаюсь достичь, возможно? Это похоже на то, что я хочу, чтобы макрос был общим, позволяя вам передать тип, который будет использоваться внутри определения макроса.

ответ

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"); 
}); 
2

ty не может быть непосредственно сопровождаться expr. Оно должно быть followed by a specific set of tokens:

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

Подобное ограничение существует после expr, stmt, path и pat. Это было введено в 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 {  
//        ^   ^
Смежные вопросы