为什么Rust有结构体和枚举?

54

Rust的枚举是代数数据类型。据我所知,这似乎包含了结构体的所有内容。那么,结构体有什么不同之处,需要保留它呢?


6
@ChrisMorgan,如果这样的东西存在(我认为不存在),那么它非常难以找到。在所有关于“struct enum”的问题中,这个问题是最接近的,但它完全不是同一个问题。 - huon
6个回答

50
首先,您是正确的,从语义上讲,enum严格优于struct,因此struct有些多余。
然而,这里还有其他元素。
  • 易用性:在enum中,只能通过匹配来直接访问值;相比之下,访问struct字段更容易使用。您可以为每个字段编写访问器,但那确实很麻烦。

  • 区别:一个enum是一个标记联合,一个struct具有固定布局;我们(程序员)通常喜欢给事物贴标签,因此赋予不同功能不同名称可能会受到赞赏。

据我所见,struct因此成为语法糖。我通常更喜欢精简和高效,但是一点点糖也可以大大增加能够简洁表示的内容。

1
注意:存在(实验性的)“结构体变量”,因此文档指向可能在未来过时。 - Manishearth
2
“enum” 严格优于 “struct” 吗?似乎 “enum” 依赖于 “struct” 和 “tuple” 来存储每个变量中的多个元素。我想,如果去掉 “struct”,但保留 “tuple”,则 “enum” 可以实现所有 “struct” 的功能。 - 8bittree
2
@8bittree:您可以将多个字段存储在单个变量或元组中,但是您无法为它们命名。 - Matthieu M.
1
嗯... 经过更深入的思考,并查看 Rust 书籍,我认为这取决于“枚举”的定义。如果将其定义为狭义的“它是这些变体之一,每个变体包含单个有效负载数据”,则它与结构体正交。如果将其定义扩展为实际在 Rust 中包含全部行为,则枚举使结构体在技术上变得多余(也包括元组)。我对此有些不舒服,因为使用狭义定义,可以通过使用结构体和元组有效负载完全复制枚举的当前行为。 - 8bittree
2
枚举并不 严格 优于结构体,它们是不同的。Lukas Kalbertodt 解释了这一点,指出枚举“无法构建产品类型”,如果数据类型具有多个独立组件需要分别访问,则使用枚举会很麻烦。 - Ultrasaurus
显示剩余6条评论

28
首先,Rust具有广泛的数据类型:
- 带有命名字段的结构体(`struct Foo {bar: uint}`) - 元组结构体(`struct Foo(pub Bar, Baz)`) - 没有字段的结构体(`struct Foo;`) - 枚举,具有各种类型的变量:
- 没有字段的变量(例如`None`) - 元组变量(例如`Some(T)`) - 结构体变量(例如`Some { pub inner :T }`)
这使得程序员在定义数据类型时具有一定的灵活性。通常,如果结构体/变量只有一个字段,您不想要命名字段。在这种情况下,Rust允许您使用元组结构体/元组变量。
如果从Rust中删除结构体,则不会损失功能,可以再次使用具有结构体变量的枚举。但是,将会产生大量单变量枚举,这将是不必要且繁琐的。

17

虽然不是百分之百正确,但另一种思考方式是: enum 并不比 struct 更优越,它只是语法糖使其看起来更优秀。

enum 是一种和类型(sum type)表示它的值是一组其他类型中的一个值。例如,Result<T, E> 类型是类型 T 或者类型 E 中的一个。因此,每个 enum 变量都与一个类型相关联。其他所有内容(无类型、元组变量和结构体变量)可能只是语法糖。

enum Animal {
    // without syntax sugar
    Cat(i32),  
    // desugars to `Dog(())` (empty tuple/unit) 
    Dog,
    // desugars to `Horse((i32, bool))` (tuple)
    Horse(i32, bool),
    // desugars to `Eagle(GeneratedEagleType)` and a struct definition outside
    // of this enum `struct GeneratedEagleType { weight: i32, male: bool }`
    Eagle { weight: i32, male: bool }
}

因此,如果每个枚举变量都与精确一个类型相关联,则足以满足要求。在这种情况下,枚举不优于结构体,因为它无法构建产品类型(如结构体)。
能够在枚举变量定义内部编写“类型定义”仅仅是为了方便。
另外:结构体也优于元组结构和元组。如果忽略名称,则这三个事物几乎等价。但是为了方便起见,Rust仍然具有这三种不同类型的类型。
请注意,我不知道这些枚举定义是否实际上是语法糖。但他们可能是,这可能会有所帮助。

3

2

除了上面的答案之外,另一个重要的区别是枚举在某一时刻可以是所述值之一,而结构体则表示该实例的所有参数的值。 例如:

enum Things{
Box, 
Page(i32),
}

struct Things{
box: Option<String>,
page: i32,
}

在上述情况下,一个单独的枚举可以是一个Box或者一个Page,而一个单独的结构体将代表一个Box和一个Page。

1
你说得对,枚举和特质以及它们通过结构体的继承实现了代数数据类型。
然而,特质是一组可扩展的类型,任何结构体都可以从任何代码中获得任何特质。使用内省,可以期望具有给定特质的值,并动态挖掘出结构体的实际类型。但是,该类型属于一组不可预测的类型,因为任何结构体都可以从任何地方获得所述特质。
而枚举一次性定义了有限的类型层次结构,使其可预测且直接匹配子类型,就像它们是简单值一样。围绕枚举的语言功能因此可以进行优化,以便在静态情况下进行类型检查,提供更高的性能和一些语言糖。但是,层次结构描述包含在枚举定义中,不影响特质类型系统。
简而言之,枚举以一种包容的方式缩小了类型层次结构,而不是依赖于特质,这些特质可以由任何代码扩展并影响所有内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接