Reputation: 786
I am having a hard time understanding the Enum
custom type in Rust. In a broad way, The Book describes an Enum
as a custom data type that has different variants. How should I think about these variants? Are these sub-types, or are these specific values that the Enum
type can take?
Looking online I see examples like:
enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
In the case above, the variants are the possible values of the Day
type. But in The Book we see examples like:
struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
To me, it seems like IpAddr::V4
is a sub-type rather than a specific value, but a variable is really of type IpAddr
and can have a value IpAddr::V4
.
Does it make sense to make the distinction I mention above? What is the correct interpretation of an Enum
?
Upvotes: 2
Views: 2550
Reputation: 823
Disclaimer: I never wrote a single line of Rust in my life, but as a Rust leaner, here's how I understand it:
Rust enums seem C enums and unions mashed together. They are similar to an union in the sense that they can store one piece of data that can be of different types in one and the same memory region. In contrast to C unions, they allow you to query the type the data in the union is of. They do this by storing this info in something called a descriminator alongside the union.
A rust enum is similar to the following in pseudo C: (Syntax is probably wrong but you get the idea.)
struct rust_union_t {
enum {int,float} discriminator,
union {int foo, float bar} data
}
In C++ you could emulate this by having struct rust_union_t
provide two constructors, one taking an int and the other one taking a float. The former sets discriminator
to int and stores the value in data
, the later sets discriminator
to float
and as well stores the value in data
.
In Rust, the compiler doesn't have you write this out.
enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
This is the special case of a rust enum in the sense that there is only a discriminator, but no actual value to store.
This link helped me understanding: https://www.eventhelix.com/rust/rust-to-assembly-enum-match/
Upvotes: 1
Reputation: 11728
Rust is a static, strongly typed language. It is also very fast. In many cases it is more efficient to use the stack, instead of the heap. However, when you use the stack Rust must know the size of the data that is needed. That's not a problem for simple fixed types like i16
, u128
, etc. It also isn't a problem for tuples, structs or arrays, because they have a fixed data structure with a known size.
However, sometimes you will need to use different data types, depending on some runtime condition/state. In languages like Java, .NET, JS, Python, PHP, etc., in such situations you will be using the heap (one way or another). In Rust you also have ways to use the heap, but that's often suboptimal. Enums in Rust allow you to define additional, variant-specific fields with custom data types. That can be very flexible and at the same time, in many cases, would be faster than solutions that make use of the heap.
Note that in languages like Java, you would often end up creating a hierarchy of classes to achieve what you can do in Rust with enums. Both approaches have their pros and cons. But if you come from a language like Java, you should keep that in mind.
Maybe a good example would be to think about how you would represent a JSON in your language of choice. If the JSON has a fixed data structure, you can use standard structs in Rust, classes in Java, etc. But what if you don't know the structure of a JSON object in advance? In most modern languages the parser would create some sort of a (Linked)HashMap that contains strings for the keys and some object instances (integers, strings, lists, maps, etc.) for the values. Compare that to serde
's Value
enum. Another example, which is not for JSON, but is conceptually similar in that you can read data of different types, is mysql
's Value
.
It might also be useful to understand how Rust allocates memory for enums. It basically determines (at compile time, of course) of all the variants, which one needs most memory. Let's say variant A
needs 12 bytes, variant B
needs 16 bytes, variant C
needs 4 bytes. Rust will allocate 16 bytes for the associated data of every enum instance, because that's the minimum size that all variants can fit in.
Upvotes: 3
Reputation: 3466
It is reasonable to see the Day
as a C-style enum. It describes all possible values of the type and has a numeric discriminant to identify each.
The IpAddr
type is a tagged union. It is some tag (a number like in the c-style enum) followed by the value you give in brackets. It is not really a subtype, more a variant of IpAddr.
Once identified by its tag (which match
and such do for you) you can use the values inside.
Upvotes: 0