如何将参数化枚举从泛型类型映射到另一个类型?

时间:2015-06-29 19:24:16

标签: rust

如果我的类型为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
}

4 个答案:

答案 0 :(得分: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 :(得分: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() {
}

但重复所有变种并不适用于其中很多变种。

答案 2 :(得分:3)

有些语言(比如C ++)使用Duck Typing:如果它像鸭子一样嘎嘎叫,它必须是鸭子,因此名称很重要。 Rust没有。

在Rust中,名称只是我们人类的一些显示效用,BMyEnum<u32>中的MyEnum<String>可能恰好具有相同的视觉表现形式,但它们完全不同于句法就语言而言,实体。

有多种方法可以减轻你的痛苦:

  • 也可以使用代码生成插件或build.rs脚本
  • 一个宏可用于自动化映射
  • 可以完成手动映射,毕竟这是一次性的努力
  • 可以重新构建代码以分离依赖于类型的变体和独立于类型的变体

我将展示后者:

enum MyEnumImpl {
    A,
    B,
    C,
}

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

显然,后者使得手动映射事物变得更加容易。

答案 3 :(得分: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)));
}