]> git.proxmox.com Git - rustc.git/blame - src/doc/book/src/ch06-01-defining-an-enum.md
New upstream version 1.55.0+dfsg1
[rustc.git] / src / doc / book / src / ch06-01-defining-an-enum.md
CommitLineData
13cf67c4
XL
1## Defining an Enum
2
3Let’s look at a situation we might want to express in code and see why enums
4are useful and more appropriate than structs in this case. Say we need to work
5with IP addresses. Currently, two major standards are used for IP addresses:
6version four and version six. These are the only possibilities for an IP
7address that our program will come across: we can *enumerate* all possible
e74abb32 8variants, which is where enumeration gets its name.
13cf67c4
XL
9
10Any IP address can be either a version four or a version six address, but not
11both at the same time. That property of IP addresses makes the enum data
e74abb32 12structure appropriate, because enum values can only be one of its variants.
13cf67c4
XL
13Both version four and version six addresses are still fundamentally IP
14addresses, so they should be treated as the same type when the code is handling
15situations that apply to any kind of IP address.
16
17We can express this concept in code by defining an `IpAddrKind` enumeration and
e74abb32
XL
18listing the possible kinds an IP address can be, `V4` and `V6`. These are the
19variants of the enum:
13cf67c4
XL
20
21```rust
74b04a01 22{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:def}}
13cf67c4
XL
23```
24
25`IpAddrKind` is now a custom data type that we can use elsewhere in our code.
26
27### Enum Values
28
29We can create instances of each of the two variants of `IpAddrKind` like this:
30
31```rust
74b04a01 32{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:instance}}
13cf67c4
XL
33```
34
35Note that the variants of the enum are namespaced under its identifier, and we
36use a double colon to separate the two. The reason this is useful is that now
37both values `IpAddrKind::V4` and `IpAddrKind::V6` are of the same type:
38`IpAddrKind`. We can then, for instance, define a function that takes any
39`IpAddrKind`:
40
41```rust
74b04a01 42{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:fn}}
13cf67c4
XL
43```
44
45And we can call this function with either variant:
46
47```rust
74b04a01 48{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-01-defining-enums/src/main.rs:fn_call}}
13cf67c4
XL
49```
50
51Using enums has even more advantages. Thinking more about our IP address type,
52at the moment we don’t have a way to store the actual IP address *data*; we
53only know what *kind* it is. Given that you just learned about structs in
69743fb6 54Chapter 5, you might tackle this problem as shown in Listing 6-1.
13cf67c4
XL
55
56```rust
74b04a01 57{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-01/src/main.rs:here}}
13cf67c4
XL
58```
59
60<span class="caption">Listing 6-1: Storing the data and `IpAddrKind` variant of
61an IP address using a `struct`</span>
62
63Here, we’ve defined a struct `IpAddr` that has two fields: a `kind` field that
64is of type `IpAddrKind` (the enum we defined previously) and an `address` field
65of type `String`. We have two instances of this struct. The first, `home`, has
66the value `IpAddrKind::V4` as its `kind` with associated address data of
67`127.0.0.1`. The second instance, `loopback`, has the other variant of
68`IpAddrKind` as its `kind` value, `V6`, and has address `::1` associated with
69it. We’ve used a struct to bundle the `kind` and `address` values together, so
70now the variant is associated with the value.
71
72We can represent the same concept in a more concise way using just an enum,
73rather than an enum inside a struct, by putting data directly into each enum
74variant. This new definition of the `IpAddr` enum says that both `V4` and `V6`
75variants will have associated `String` values:
76
77```rust
74b04a01 78{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-02-enum-with-data/src/main.rs:here}}
13cf67c4
XL
79```
80
81We attach data to each variant of the enum directly, so there is no need for an
82extra struct.
83
84There’s another advantage to using an enum rather than a struct: each variant
85can have different types and amounts of associated data. Version four type IP
86addresses will always have four numeric components that will have values
87between 0 and 255. If we wanted to store `V4` addresses as four `u8` values but
88still express `V6` addresses as one `String` value, we wouldn’t be able to with
89a struct. Enums handle this case with ease:
90
91```rust
74b04a01 92{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-03-variants-with-different-data/src/main.rs:here}}
13cf67c4
XL
93```
94
95We’ve shown several different ways to define data structures to store version
96four and version six IP addresses. However, as it turns out, wanting to store
97IP addresses and encode which kind they are is so common that [the standard
98library has a definition we can use!][IpAddr]<!-- ignore --> Let’s look at how
99the standard library defines `IpAddr`: it has the exact enum and variants that
100we’ve defined and used, but it embeds the address data inside the variants in
101the form of two different structs, which are defined differently for each
102variant:
103
104[IpAddr]: ../std/net/enum.IpAddr.html
105
106```rust
107struct Ipv4Addr {
108 // --snip--
109}
110
111struct Ipv6Addr {
112 // --snip--
113}
114
115enum IpAddr {
116 V4(Ipv4Addr),
117 V6(Ipv6Addr),
118}
119```
120
121This code illustrates that you can put any kind of data inside an enum variant:
122strings, numeric types, or structs, for example. You can even include another
123enum! Also, standard library types are often not much more complicated than
124what you might come up with.
125
126Note that even though the standard library contains a definition for `IpAddr`,
127we can still create and use our own definition without conflict because we
128haven’t brought the standard library’s definition into our scope. We’ll talk
129more about bringing types into scope in Chapter 7.
130
131Let’s look at another example of an enum in Listing 6-2: this one has a wide
69743fb6 132variety of types embedded in its variants.
13cf67c4
XL
133
134```rust
74b04a01 135{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-02/src/main.rs:here}}
13cf67c4
XL
136```
137
138<span class="caption">Listing 6-2: A `Message` enum whose variants each store
139different amounts and types of values</span>
140
141This enum has four variants with different types:
142
143* `Quit` has no data associated with it at all.
136023e0 144* `Move` has named fields like a struct does.
13cf67c4
XL
145* `Write` includes a single `String`.
146* `ChangeColor` includes three `i32` values.
147
69743fb6 148Defining an enum with variants such as the ones in Listing 6-2 is similar to
13cf67c4
XL
149defining different kinds of struct definitions, except the enum doesn’t use the
150`struct` keyword and all the variants are grouped together under the `Message`
151type. The following structs could hold the same data that the preceding enum
152variants hold:
153
154```rust
74b04a01 155{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-04-structs-similar-to-message-enum/src/main.rs:here}}
13cf67c4
XL
156```
157
158But if we used the different structs, which each have their own type, we
159couldn’t as easily define a function to take any of these kinds of messages as
160we could with the `Message` enum defined in Listing 6-2, which is a single type.
161
162There is one more similarity between enums and structs: just as we’re able to
163define methods on structs using `impl`, we’re also able to define methods on
164enums. Here’s a method named `call` that we could define on our `Message` enum:
165
166```rust
74b04a01 167{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-05-methods-on-enums/src/main.rs:here}}
13cf67c4
XL
168```
169
170The body of the method would use `self` to get the value that we called the
171method on. In this example, we’ve created a variable `m` that has the value
172`Message::Write(String::from("hello"))`, and that is what `self` will be in the
173body of the `call` method when `m.call()` runs.
174
175Let’s look at another enum in the standard library that is very common and
176useful: `Option`.
177
178### The `Option` Enum and Its Advantages Over Null Values
179
180In the previous section, we looked at how the `IpAddr` enum let us use Rust’s
181type system to encode more information than just the data into our program.
182This section explores a case study of `Option`, which is another enum defined
183by the standard library. The `Option` type is used in many places because it
184encodes the very common scenario in which a value could be something or it
185could be nothing. Expressing this concept in terms of the type system means the
186compiler can check whether you’ve handled all the cases you should be handling;
187this functionality can prevent bugs that are extremely common in other
188programming languages.
189
190Programming language design is often thought of in terms of which features you
191include, but the features you exclude are important too. Rust doesn’t have the
192null feature that many other languages have. *Null* is a value that means there
193is no value there. In languages with null, variables can always be in one of
194two states: null or not-null.
195
196In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony
197Hoare, the inventor of null, has this to say:
198
199> I call it my billion-dollar mistake. At that time, I was designing the first
200> comprehensive type system for references in an object-oriented language. My
201> goal was to ensure that all use of references should be absolutely safe, with
202> checking performed automatically by the compiler. But I couldn’t resist the
203> temptation to put in a null reference, simply because it was so easy to
204> implement. This has led to innumerable errors, vulnerabilities, and system
205> crashes, which have probably caused a billion dollars of pain and damage in
206> the last forty years.
207
208The problem with null values is that if you try to use a null value as a
209not-null value, you’ll get an error of some kind. Because this null or not-null
210property is pervasive, it’s extremely easy to make this kind of error.
211
212However, the concept that null is trying to express is still a useful one: a
213null is a value that is currently invalid or absent for some reason.
214
215The problem isn’t really with the concept but with the particular
216implementation. As such, Rust does not have nulls, but it does have an enum
217that can encode the concept of a value being present or absent. This enum is
218`Option<T>`, and it is [defined by the standard library][option]<!-- ignore -->
219as follows:
220
221[option]: ../std/option/enum.Option.html
222
223```rust
224enum Option<T> {
13cf67c4 225 None,
136023e0 226 Some(T),
13cf67c4
XL
227}
228```
229
230The `Option<T>` enum is so useful that it’s even included in the prelude; you
231don’t need to bring it into scope explicitly. In addition, so are its variants:
232you can use `Some` and `None` directly without the `Option::` prefix. The
233`Option<T>` enum is still just a regular enum, and `Some(T)` and `None` are
234still variants of type `Option<T>`.
235
236The `<T>` syntax is a feature of Rust we haven’t talked about yet. It’s a
237generic type parameter, and we’ll cover generics in more detail in Chapter 10.
238For now, all you need to know is that `<T>` means the `Some` variant of the
239`Option` enum can hold one piece of data of any type. Here are some examples of
240using `Option` values to hold number types and string types:
241
242```rust
74b04a01 243{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-06-option-examples/src/main.rs:here}}
13cf67c4
XL
244```
245
246If we use `None` rather than `Some`, we need to tell Rust what type of
247`Option<T>` we have, because the compiler can’t infer the type that the `Some`
248variant will hold by looking only at a `None` value.
249
250When we have a `Some` value, we know that a value is present and the value is
251held within the `Some`. When we have a `None` value, in some sense, it means
252the same thing as null: we don’t have a valid value. So why is having
253`Option<T>` any better than having null?
254
255In short, because `Option<T>` and `T` (where `T` can be any type) are different
256types, the compiler won’t let us use an `Option<T>` value as if it were
257definitely a valid value. For example, this code won’t compile because it’s
258trying to add an `i8` to an `Option<i8>`:
259
260```rust,ignore,does_not_compile
74b04a01 261{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/src/main.rs:here}}
13cf67c4
XL
262```
263
264If we run this code, we get an error message like this:
265
f035d41b 266```console
74b04a01 267{{#include ../listings/ch06-enums-and-pattern-matching/no-listing-07-cant-use-option-directly/output.txt}}
13cf67c4
XL
268```
269
270Intense! In effect, this error message means that Rust doesn’t understand how
271to add an `i8` and an `Option<i8>`, because they’re different types. When we
272have a value of a type like `i8` in Rust, the compiler will ensure that we
273always have a valid value. We can proceed confidently without having to check
274for null before using that value. Only when we have an `Option<i8>` (or
275whatever type of value we’re working with) do we have to worry about possibly
276not having a value, and the compiler will make sure we handle that case before
277using the value.
278
279In other words, you have to convert an `Option<T>` to a `T` before you can
280perform `T` operations with it. Generally, this helps catch one of the most
281common issues with null: assuming that something isn’t null when it actually
282is.
283
284Not having to worry about incorrectly assuming a not-null value helps you to be
285more confident in your code. In order to have a value that can possibly be
286null, you must explicitly opt in by making the type of that value `Option<T>`.
287Then, when you use that value, you are required to explicitly handle the case
288when the value is null. Everywhere that a value has a type that isn’t an
289`Option<T>`, you *can* safely assume that the value isn’t null. This was a
290deliberate design decision for Rust to limit null’s pervasiveness and increase
291the safety of Rust code.
292
293So, how do you get the `T` value out of a `Some` variant when you have a value
294of type `Option<T>` so you can use that value? The `Option<T>` enum has a large
295number of methods that are useful in a variety of situations; you can check
296them out in [its documentation][docs]<!-- ignore -->. Becoming familiar with
297the methods on `Option<T>` will be extremely useful in your journey with Rust.
298
299[docs]: ../std/option/enum.Option.html
300
301In general, in order to use an `Option<T>` value, you want to have code that
302will handle each variant. You want some code that will run only when you have a
303`Some(T)` value, and this code is allowed to use the inner `T`. You want some
304other code to run if you have a `None` value, and that code doesn’t have a `T`
305value available. The `match` expression is a control flow construct that does
306just this when used with enums: it will run different code depending on which
307variant of the enum it has, and that code can use the data inside the matching
308value.