【问题标题】:What is the correct way to write the match statement for an enum with already defined structs?为已定义结构的枚举编写 match 语句的正确方法是什么?
【发布时间】:2020-03-18 17:55:30
【问题描述】:

我希望枚举的行为类似于我之前定义的结构的变体:

pub struct Element {
    symbol: String,
    atomic_number: u8,
    atomic_mass: f32,
}
pub struct Hydrogen {
    element: Element,
}
pub struct Helium {
    element: Element,
}
pub struct Lithium {
    element: Element,
}
pub enum ElementKind {
    HYDROGEN(Hydrogen),
    HELIUM(Helium),
    LITHIUM(Lithium,
}

impl Default for Hydrogen {
    fn default() -> Self {
        Hydrogen {
            element: Element {
                symbol: "H".to_string(),
                atomic_number: 1,
                atomic_mass: 1.008,
            },
        }
    }
}

impl Default for Helium {
    fn default() -> Self {
        Helium {
            element: Element {
                symbol: "He".to_string(),
                atomic_number: 2,
                atomic_mass: 4.003,
            },
        }
    }
}

impl Default for Lithium {
    fn default() -> Self {
        Lithium {
            element: Element {
                symbol: "Li".to_string(),
                atomic_number: 3,
                atomic_mass: 6.491,
            },
        }
    }
}

fn main() {
    let e = ElementKind::HYDROGEN;
    match e {
        // TODO
    }
}

编写match 语句以便始终打印出元素的symbol 的正确方法是什么?

【问题讨论】:

  • 您期望element 是什么?您永远不会使用该名称声明变量。看起来您可能将变体 ElementKind::Hydrogen 与结构 Hydrogen 混淆了。
  • 应该是e.symbol
  • 你认为你在哪里创造了一个Hydrogen类型的值?
  • @nyarlathotep108 查看我以前的 cmets。 ElementKind::HydrogenHydrogen 是不同的类型。
  • Is there a way to use existing structs as enum variants?的答案可能会回答您的问题。如果没有,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。

标签: struct enums rust pattern-matching


【解决方案1】:

当你写作时

struct Foo {
    foo: u32,
}

enum Bar {
    Foo,
}

Foo 结构和Bar::Foo 变体之间绝对没有关系。他们只是碰巧有相同的名字。 (另见Is there a way to use existing structs as enum variants?)。

解决这个问题的常用方法是在变体中添加一个字段:

enum Bar {
    // The first Foo is the variant name, the second Foo a field of type Foo
    Foo(Foo),
}

或在枚举中内联结构(即使用struct 变体):

enum Bar {
    Foo {
        foo: u32,
    }
}

但是,在您的情况下,我相信您不需要为每个元素创建一个结构,只需执行以下操作:

#[derive(Debug)]
pub struct Element {
    symbol: String,
    atomic_number: u8,
    atomic_mass: f32,
}

pub enum ElementKind {
    Hydrogen,
    Helium,
    Lithium,
}

impl From<ElementKind> for Element {
    fn from(e: ElementKind) -> Element {
        use ElementKind::*;

        match e {
            Hydrogen => Element {
                symbol: "H".to_string(),
                atomic_number: 1,
                atomic_mass: 1.008,
            },
            Helium => Element {
                symbol: "He".to_string(),
                atomic_number: 2,
                atomic_mass: 4.003,
            },
            Lithium => Element {
                symbol: "Li".to_string(),
                atomic_number: 3,
                atomic_mass: 6.491,
            },
        }
    }
}

fn main() {
    let e = ElementKind::Hydrogen;
    println!("{:#?}", Element::from(e));
}

(Permalink to the playground)

确实,使用您的解决方案,ElementKind 的每个实例都将包含冗余信息:变体本身(足以识别元素)及其数据(也足以识别元素)。

【讨论】:

  • 是的,我有点想在 enum 中使用一些互斥常量,就像 Java enum 类一样,它们有字段,但使用 variant / tagged union 方法,因为 Rust 确实如此真的没有enum 课程……确实很乱。您的解决方案更清洁。我会研究这个From trait。
  • Rust 枚举比 Java 的要强大得多。 Java 枚举只是美化的常量。字段不会改变这一点。 Here is a Rust example similar to using Java's enum with fields.
  • 我大体上同意,但是例如在您链接的代码中,您并不能真正表示这些类型之间的互斥,您仍然可以在其字段中构造一个具有随机值的元素。跨度>
  • 然后将您的构造函数设为私有。
  • 我觉得还是有点不一样。我认为语言中缺少的是,给定一个enum,其字段都是相同的struct 类型,能够用值初始化那些enum 字段。当enum 字段是普通整数时,这是可能的,但显然它不适用于struct
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-12
  • 1970-01-01
  • 1970-01-01
  • 2016-07-06
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
相关资源
最近更新 更多