2015-06-29 1 views
4

Если у меня есть такой тип, как MyEnum<T>, как его сопоставить в случаях, когда не каждый параметр параметризуется?Как сопоставить параметризованное перечисление от общего типа к другому?

Например, я хотел бы преобразовать из MyEnum<u32> в MyEnum<String>:

enum MyEnum<T> { 
    B, 
    C, 
    D(T), 
} 

fn trans(a: MyEnum<u32>) -> MyEnum<String> { 
    match a { 
     MyEnum::D(i) => MyEnum::D(i.to_string()), 
     other_cases => other_cases, 
    } 
} 

fn main() {} 

Это терпит неудачу с:

error[E0308]: match arms have incompatible types 
    --> src/main.rs:8:9 
    | 
8 |   match a { 
    |  ^expected struct `std::string::String`, found u32 
    | 
    = note: expected type `MyEnum<std::string::String>` 
    = note: found type `MyEnum<u32>` 
note: match arm with an incompatible type 
    --> src/main.rs:10:28 
    | 
10 |    other_cases => other_cases, 
    |       ^^^^^^^^^^^ 

Вместо этого other_cases => other_cases линии, я попробовал это, также без успеха:

other_cases => { 
    let o: MyEnum<String> = other_cases; 
    o 
} 
+1

'MyEnum ' и '' MyEnum два различных типа, поэтому их варианты не являются взаимозаменяемыми. –

ответ

2
macro_rules! partial_enum { 
    ($name: ident, $some: ident, $($none: ident),+) => { 
     #[derive(Debug)] 
     enum $name<T> { 
      $some(T), 
      $($none),+ 
     } 
     impl<T> $name<T> { 
      fn convert<U>(self) -> Result<$name<U>, T> { 
       match self { 
        $name::$some(x) => Err(x), 
        $($name::$none => Ok($name::$none)),+ 
       } 
      } 
     } 
    } 
} 
partial_enum!(MyEnum, D, B, C); 
fn trans(a: MyEnum<u32>) -> MyEnum<String> { 
    let a_split: Result<MyEnum<String>, u32> = a.convert(); 
    match a_split { 
     Ok(is_none) => is_none, 
     Err(not_none) => MyEnum::D(not_none.to_string()), 
    } 
} 
fn main() { 
    println!("{:?}", trans(MyEnum::D(13))); 
} 
5

Ну, это на самом деле ответ :

enum MyEnum<T> { 
    B, 
    C, 
    D(T), 
} 

fn trans(a: MyEnum<u32>) -> MyEnum<String> { 
    match a { 
     MyEnum::D(i) => MyEnum::D(i.to_string()), 
     MyEnum::B => MyEnum::B, 
     MyEnum::C => MyEnum::C 
    } 
} 

fn main() { 
} 

Но повторять все варианты не приемлемы, когда есть много из них ..

+1

Это именно то, что вам нужно сделать. Для больших случаев вы можете создать макрос, который помогает. –

7

Я бы создать метод map на вашем перечислении:

#[derive(Debug)] 
enum MyEnum<T> { 
    B, 
    C, 
    D(T), 
} 

impl<T> MyEnum<T> { 
    fn map<F, U>(self, f: F) -> MyEnum<U> 
     where F: FnOnce(T) -> U 
    { 
     use MyEnum::*; 

     match self { 
      B => B, 
      C => C, 
      D(x) => D(f(x)), 
     } 
    } 
} 

fn main() { 
    let answer = MyEnum::D(42); 
    let answer2 = answer.map(|x| x.to_string()); 
    println!("{:?}", answer2); 
} 

Это похоже на существующие методы map, такие как Option::map.

+1

Это должен был быть принятый ответ. – WiSaGaN

2

Некоторые языки (например, C++), используют Duck Typing: если это ошеломляет, как утка, это должно быть утка, и поэтому называет материю. Руста нет.

В Руст, имена лишь некоторые дисплей утилита для нас, простых людей, то B в MyEnum<u32> и MyEnum<String> может случиться, чтобы иметь такое же визуальное представление, но они совершенно разные синтаксические сущности, насколько язык обеспокоен.


Есть несколько способов, чтобы облегчить вашу боль, хотя:

  • плагин генерации кода или build.rs сценарий может быть использован, а
  • макрос может быть использован для автоматизации отображения
  • ручное сопоставление может быть сделано, это одноразовое усилие в конце концов
  • код может быть изменен, чтобы отделить зависящие от типа варианты, не зависящие от типа варианты

Я витрина последняя:

enum MyEnumImpl { 
    A, 
    B, 
    C, 
} 

enum MyEnum<T> { 
    Independent(MyEnumImpl), 
    Dependent(T), 
} 

Очевидно, что последний делает его гораздо проще вручную сопоставить вещи.

Смежные вопросы