Rearrange sections to match the structure change made in the docx

This also makes chapter 6 more like the other chapters, with the intro
page pretty short.
This commit is contained in:
Carol (Nichols || Goulding) 2017-01-27 21:02:03 -05:00
parent d1aa4d806e
commit 4888172a74
4 changed files with 416 additions and 415 deletions

View File

@ -24,7 +24,7 @@
- [Method Syntax](ch05-01-method-syntax.md)
- [Enums](ch06-00-enums.md)
- [Option](ch06-01-option.md)
- [Defining an Enum](ch06-01-defining-an-enum.md)
- [Match](ch06-02-match.md)
- [`if let`](ch06-03-if-let.md)

View File

@ -13,276 +13,3 @@ code.
Enums are a feature in many languages, but their capabilities differ
per-language. Rusts enums are most similar to "algebraic data types" in
functional languages like F#, OCaml, or Haskell.
## Defining an Enum
Let's look at a situation we might want to express in code and see why enums are
sometimes a more appropriate choice than structs for modeling data. Say we need
to work with IP addresses. There are two major standards used for IP addresses
today: version four and version six. These are the only possibilities for an IP
address that our program will come across: we can *enumerate* all possible
values, which is where *enumeration* gets its name.
Any IP address can be either a version four or a version six address, but not
both at the same time. That property of IP addresses makes the enum data
structure appropriate for this case, since enum values can only be one of the
variants. Both version four and version six addresses are still fundamentally
IP addresses, though, so they should be treated as the same type when the code
is handling situations that apply to any kind of IP address.
We can express this concept in code by defining an `IpAddrKind` enumeration and
listing the possible kinds an IP address can be, `V4` and `V6`. These are known
as the *variants* of the enum:
```rust
enum IpAddrKind {
V4,
V6,
}
```
This is now a custom data type that we can use elsewhere in our code.
### Enum Values
<!-- Option 1: -->
An `enum` definition is similar to a `struct` definition: it defines a new type
and a template of what instances of that new type will be like. When you want to
use a struct, you create an instance of the struct. When you want to use an
enum, you create an instance of the enum that is one of the variants the enum
allows.
<!-- Option 2: -->
When you want to use a struct, you create an instance of the *struct* itself.
When you want to use an enum, you create an instance of one of its *variants*.
Each variant is defined like a struct, and you instantiate both using the same
syntax.
<!-- end options -->
We can create instances of each of the two variants of `IpAddrKind` like this:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
```
Note that the variants of the enum are namespaced under its identifier, and we
use the double colon to separate the two. The reason this is useful is that now
both values `IpAddrKind::V4` and `IpAddrKind::V6` are of the same type:
`IpAddrKind`. We can then, for instance, define a function that takes any
`IpAddrKind`:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
fn route(ip_type: IpAddrKind) { }
```
And we can call this function with either variant:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
# fn route(ip_type: IpAddrKind) { }
#
route(IpAddrKind::V4);
route(IpAddrKind::V6);
```
Enums have more tricks up their sleeves, too. Thinking more about our IP
address type, at the moment we dont have a way to store the actual *data* of
the IP address; we only know what *kind* it is. Given that you just learned
about structs, you might tackle this problem as in Listing 6-1:
<figure>
```rust
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
```
<figcaption>
Listing 6-1: Storing the data and type of an IP address using a `struct`
</figcaption>
</figure>
Here, we've defined a struct `IpAddr` that has two fields: a `kind` field that
is of type `IpAddrKind` (the enum we defined previously), and an `address`
field of type `String`. We have two instances of this struct. The first,
`home`, has the value `IpAddrKind::V4` as its `kind`, with associated address
data of `127.0.0.1`. The second instance, `loopback`, has the other variant of
`IpAddrKind` as its `kind` value, `V6`, and has address `::1` associated with
it. Weve used a struct to bundle the `kind` and `address` values together, so
that now the kind is associated with the value itself.
We can represent the same concept in a more concise way using just an enum,
rather than an enum inside a struct, by putting data directly into each enum
variant. This new definition of the `IpAddr` enum says that both `V4` and `V6`
variants will have associated `String` values:
```rust
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
```
We attach data to each variant of the enum directly, no need for an extra
struct.
There's another advantage to using an enum over a struct: each variant can
store *different kinds* of data. Version four type IP addresses will always
have four numeric components that will have values between 0 and 255. If we
wanted to store `V4` addresses as four `u8`s but still express `V6` addresses
as `String`s, we wouldn't be able to with a `struct`. Enums handle this case
with ease:
```rust
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
```
We've been showing a bunch of different possibilities that we could define in
our code for storing IP addresses of the two different kinds using an enum. It
turns out, though, that wanting to store IP addresses and encode which kind
they are is so common that [the standard library has a definition we can
use!][IpAddr]<!-- ignore --> Let's look at how the standard library defines
`IpAddr`: it has the exact enum and variants that we've defined and used, but
it chose to embed the address data inside the variants in the form of two
different structs, which are defined differently for each variant:
[IpAddr]: ../std/net/enum.IpAddr.html
```rust
struct Ipv4Addr {
// details elided
}
struct Ipv6Addr {
// details elided
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
```
This illustrates you can put any kind of data inside of an enum variant:
strings, numeric types, structs, and you could even include another enum! Also,
standard library types are often not much more complicated than what you might
come up with.
Note that even though the standard library contains a definition for `IpAddr`,
we can still choose to create and use our own definition without conflict since
we haven't brought the standard library's definition into our scope. We'll talk
more about importing types in Chapter 7.
Let's look at another example: heres an enum with a wide variety of types
embedded in its variants:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
```
This enum has four variants with different types:
* `Quit` has no data associated with it at all.
* `Move` includes an anonymous struct inside of it.
* `Write` includes a single `String`.
* `ChangeColor` includes three `i32`s.
This is similar to different kinds of struct definitions, except without the
`struct` keyword and all grouped together under the `Message` type. The
following structs could hold the same data that the enum variants above hold:
```rust
struct QuitMessage; // unit struct
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct
```
But if we used the different structs, which each have their own type, we
wouldn't be able to as easily define a function that could take any of these
kinds of messages as we could with the `Message` enum defined above, which is a
single type.
One more similarity between enums and structs: just as we are able to define
methods on structs using `impl`, we are also able to define methods on enums.
Here's a method, `call`, that we could define on our `Message` enum:
```rust
# enum Message {
# Quit,
# Move { x: i32, y: i32 },
# Write(String),
# ChangeColor(i32, i32, i32),
# }
#
impl Message {
fn call(&self) {
// body would be defined here
}
}
let m = Message::Write(String::from("hello"));
m.call();
```
The body of the method would use `self` to get the value that we called the
method on. In this example, we've created a variable `m` that has the value
`Message::Write("hello")`, and that is what `self` will be in the body of
the `call` method when `m.call()` runs.
Let's look at another enum in the standard library that is very common and
useful: `Option`.

View File

@ -0,0 +1,415 @@
## Defining an Enum
Let's look at a situation we might want to express in code and see why enums are
sometimes a more appropriate choice than structs for modeling data. Say we need
to work with IP addresses. There are two major standards used for IP addresses
today: version four and version six. These are the only possibilities for an IP
address that our program will come across: we can *enumerate* all possible
values, which is where *enumeration* gets its name.
Any IP address can be either a version four or a version six address, but not
both at the same time. That property of IP addresses makes the enum data
structure appropriate for this case, since enum values can only be one of the
variants. Both version four and version six addresses are still fundamentally
IP addresses, though, so they should be treated as the same type when the code
is handling situations that apply to any kind of IP address.
We can express this concept in code by defining an `IpAddrKind` enumeration and
listing the possible kinds an IP address can be, `V4` and `V6`. These are known
as the *variants* of the enum:
```rust
enum IpAddrKind {
V4,
V6,
}
```
This is now a custom data type that we can use elsewhere in our code.
### Enum Values
<!-- Option 1: -->
An `enum` definition is similar to a `struct` definition: it defines a new type
and a template of what instances of that new type will be like. When you want to
use a struct, you create an instance of the struct. When you want to use an
enum, you create an instance of the enum that is one of the variants the enum
allows.
<!-- Option 2: -->
When you want to use a struct, you create an instance of the *struct* itself.
When you want to use an enum, you create an instance of one of its *variants*.
Each variant is defined like a struct, and you instantiate both using the same
syntax.
<!-- end options -->
We can create instances of each of the two variants of `IpAddrKind` like this:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
```
Note that the variants of the enum are namespaced under its identifier, and we
use the double colon to separate the two. The reason this is useful is that now
both values `IpAddrKind::V4` and `IpAddrKind::V6` are of the same type:
`IpAddrKind`. We can then, for instance, define a function that takes any
`IpAddrKind`:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
fn route(ip_type: IpAddrKind) { }
```
And we can call this function with either variant:
```rust
# enum IpAddrKind {
# V4,
# V6,
# }
#
# fn route(ip_type: IpAddrKind) { }
#
route(IpAddrKind::V4);
route(IpAddrKind::V6);
```
Enums have more tricks up their sleeves, too. Thinking more about our IP
address type, at the moment we dont have a way to store the actual *data* of
the IP address; we only know what *kind* it is. Given that you just learned
about structs, you might tackle this problem as in Listing 6-1:
<figure>
```rust
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
```
<figcaption>
Listing 6-1: Storing the data and type of an IP address using a `struct`
</figcaption>
</figure>
Here, we've defined a struct `IpAddr` that has two fields: a `kind` field that
is of type `IpAddrKind` (the enum we defined previously), and an `address`
field of type `String`. We have two instances of this struct. The first,
`home`, has the value `IpAddrKind::V4` as its `kind`, with associated address
data of `127.0.0.1`. The second instance, `loopback`, has the other variant of
`IpAddrKind` as its `kind` value, `V6`, and has address `::1` associated with
it. Weve used a struct to bundle the `kind` and `address` values together, so
that now the kind is associated with the value itself.
We can represent the same concept in a more concise way using just an enum,
rather than an enum inside a struct, by putting data directly into each enum
variant. This new definition of the `IpAddr` enum says that both `V4` and `V6`
variants will have associated `String` values:
```rust
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
```
We attach data to each variant of the enum directly, no need for an extra
struct.
There's another advantage to using an enum over a struct: each variant can
store *different kinds* of data. Version four type IP addresses will always
have four numeric components that will have values between 0 and 255. If we
wanted to store `V4` addresses as four `u8`s but still express `V6` addresses
as `String`s, we wouldn't be able to with a `struct`. Enums handle this case
with ease:
```rust
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
```
We've been showing a bunch of different possibilities that we could define in
our code for storing IP addresses of the two different kinds using an enum. It
turns out, though, that wanting to store IP addresses and encode which kind
they are is so common that [the standard library has a definition we can
use!][IpAddr]<!-- ignore --> Let's look at how the standard library defines
`IpAddr`: it has the exact enum and variants that we've defined and used, but
it chose to embed the address data inside the variants in the form of two
different structs, which are defined differently for each variant:
[IpAddr]: ../std/net/enum.IpAddr.html
```rust
struct Ipv4Addr {
// details elided
}
struct Ipv6Addr {
// details elided
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
```
This illustrates you can put any kind of data inside of an enum variant:
strings, numeric types, structs, and you could even include another enum! Also,
standard library types are often not much more complicated than what you might
come up with.
Note that even though the standard library contains a definition for `IpAddr`,
we can still choose to create and use our own definition without conflict since
we haven't brought the standard library's definition into our scope. We'll talk
more about importing types in Chapter 7.
Let's look at another example: heres an enum with a wide variety of types
embedded in its variants:
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
```
This enum has four variants with different types:
* `Quit` has no data associated with it at all.
* `Move` includes an anonymous struct inside of it.
* `Write` includes a single `String`.
* `ChangeColor` includes three `i32`s.
This is similar to different kinds of struct definitions, except without the
`struct` keyword and all grouped together under the `Message` type. The
following structs could hold the same data that the enum variants above hold:
```rust
struct QuitMessage; // unit struct
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct
```
But if we used the different structs, which each have their own type, we
wouldn't be able to as easily define a function that could take any of these
kinds of messages as we could with the `Message` enum defined above, which is a
single type.
One more similarity between enums and structs: just as we are able to define
methods on structs using `impl`, we are also able to define methods on enums.
Here's a method, `call`, that we could define on our `Message` enum:
```rust
# enum Message {
# Quit,
# Move { x: i32, y: i32 },
# Write(String),
# ChangeColor(i32, i32, i32),
# }
#
impl Message {
fn call(&self) {
// body would be defined here
}
}
let m = Message::Write(String::from("hello"));
m.call();
```
The body of the method would use `self` to get the value that we called the
method on. In this example, we've created a variable `m` that has the value
`Message::Write("hello")`, and that is what `self` will be in the body of
the `call` method when `m.call()` runs.
Let's look at another enum in the standard library that is very common and
useful: `Option`.
## The `Option` Enum and its Advantages Over Null Values
In the previous section, we looked at how the `IpAddr` enum let us use Rust's
type system to encode more information than just the data into our program.
This section is a case study of `Option`, which is another enum defined by the
standard library. The `Option` type is used in many places because it encodes
the very common scenario in which a value could be *something* or it could be
*nothing*. Expressing this concept in terms of the type system means the
compiler can check that you've handled all the cases you should be handling,
which can prevent bugs that are extremely common in other programming languages.
Programming language design is often thought of in terms of which features you
include, but the features you leave out are important too. Rust does not have
the *null* feature that many other languages have. Null is a value that means
there is no value there. In languages with null, variables can always be in one
of two states: null or not-null.
The inventor of null has this to say:
> I call it my billion-dollar mistake. At that time, I was designing the first
> comprehensive type system for references in an object-oriented language. My
> goal was to ensure that all use of references should be absolutely safe, with
> checking performed automatically by the compiler. But I couldn't resist the
> temptation to put in a null reference, simply because it was so easy to
> implement. This has led to innumerable errors, vulnerabilities, and system
> crashes, which have probably caused a billion dollars of pain and damage in
> the last forty years.
>
> - Tony Hoare "Null References: The Billion Dollar Mistake"
The problem with null values is that if you try to actually use a value that's
null as if it is a not-null value, you'll get an error of some kind. Because
this null or not-null property is pervasive, it's extremely easy to make this
kind of error.
The concept that null is trying to express is still a useful one, however: a
null is a value which is currently invalid or absent for some reason.
The problem isn't with the concept itself, but with the particular
implementation. As such, Rust does not have nulls, but it does have an enum
that can encode the concept of a value being present or absent. This enum is
`Option<T>`, and it is [defined by the standard library][option]<!-- ignore -->
as follows:
[option]: ../std/option/enum.Option.html
```rust
enum Option<T> {
Some(T),
None,
}
```
The `Option<T>` enum is so useful that it's even included in the prelude; you
don't need to import it explicitly. Furthermore, so are its variants: you can
use `Some` and `None` directly, without prefixing them with `Option::`.
`Option<T>` is still just a regular enum, however, and `Some(T)` and `None` are
still values of type `Option<T>`.
The `<T>` syntax is a feature of Rust we haven't talked about yet. It's a
generic type parameter, and we'll cover generics in more detail in Chapter 10.
For now, all you need to know is that this means the `Some` variant of the
`Option` enum can hold one piece of data of any type. Here are some examples of
using `Option` values to hold number types and string types:
```rust
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
```
If we use `None` rather than `Some`, we need to tell Rust what type of
`Option<T>` we have, because the compiler can't infer the type that the `Some`
variant will hold by looking at the `None` variant.
When we have a `Some` value, we know that there is a value present, and the
value is held within the `Some`. When we have a `None` value, in some sense,
that means the same thing that null does: we do not have a valid value. So why
is this any better than null?
In short, because `Option<T>` and `T` (where `T` can be any type) are different
types from each other, so the compiler won't let us use an `Option` value as if
it was definitely a valid value. For example, this code won't compile because
it's trying to compare an `Option<i8>` to an `i8`:
```rust,ignore
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
```
If we run this code, we get an error message like this:
```text
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is not satisfied
-->
|
7 | let sum = x + y;
| ^^^^^
|
```
Intense! What this error message is trying to say is that Rust does not
understand how to add an `Option<i8>` and an `i8`, since they're different
types. When we have a value of a type like `i8` in Rust, the compiler will
ensure that we always have a valid value. We can proceed confidently without
having to check for null before using that value. Only when we have an
`Option<i8>` (or whatever type of value we're working with) do we have to
worry about possibly not having a value, and the compiler will make sure we
handle that case before using the value.
In other words, you have to convert an `Option<T>` to a `T` before you can do
`T` stuff with it. This helps catch one of the most common issues with null,
generally: assuming that something isn't null when it actually is.
This is pretty powerful: in order to have a value that can possibly be null,
you have to explicitly opt in by making the type of that value `Option<T>`.
Then, when you use that value, you are required to explicitly handle the case
when the value is null. Everywhere that a value has a type that isn't an
`Option<T>`, you *can* safely assume that the value isn't null. This was a
deliberate design decision for Rust to limit null's pervasiveness and increase
the safety of Rust code.
So, how *do* you get the `T` value out of a `Some` variant when you have a
value of type `Option<T>` so that you can use that value? The `Option<T>` enum
has a large number of methods useful in a variety of situations that you can
check out in [its documentation][docs]<!-- ignore -->, and becoming familiar
with them will be extremely useful in your journey with Rust.
[docs]: ../std/option/enum.Option.html
What we generally want to do in order to use an `Option<T>` value is to have
code that will handle each variant. We want some code that will run only in the
case that we have a `Some(T)` value, and this code *is* allowed to use the
inner `T`. We want some *other* code to run if we have a `None` value, and that
code doesn't have a `T` value available. The `match` expression is a control
flow construct that does just this, when used with enums: it will run different
code depending on which variant of the enum it has, and that code can use the
data inside the matching value.

View File

@ -1,141 +0,0 @@
## The `Option` Enum and its Advantages Over Null Values
In the previous section, we looked at how the `IpAddr` enum let us use Rust's
type system to encode more information than just the data into our program.
This section is a case study of `Option`, which is another enum defined by the
standard library. The `Option` type is used in many places because it encodes
the very common scenario in which a value could be *something* or it could be
*nothing*. Expressing this concept in terms of the type system means the
compiler can check that you've handled all the cases you should be handling,
which can prevent bugs that are extremely common in other programming languages.
Programming language design is often thought of in terms of which features you
include, but the features you leave out are important too. Rust does not have
the *null* feature that many other languages have. Null is a value that means
there is no value there. In languages with null, variables can always be in one
of two states: null or not-null.
The inventor of null has this to say:
> I call it my billion-dollar mistake. At that time, I was designing the first
> comprehensive type system for references in an object-oriented language. My
> goal was to ensure that all use of references should be absolutely safe, with
> checking performed automatically by the compiler. But I couldn't resist the
> temptation to put in a null reference, simply because it was so easy to
> implement. This has led to innumerable errors, vulnerabilities, and system
> crashes, which have probably caused a billion dollars of pain and damage in
> the last forty years.
>
> - Tony Hoare "Null References: The Billion Dollar Mistake"
The problem with null values is that if you try to actually use a value that's
null as if it is a not-null value, you'll get an error of some kind. Because
this null or not-null property is pervasive, it's extremely easy to make this
kind of error.
The concept that null is trying to express is still a useful one, however: a
null is a value which is currently invalid or absent for some reason.
The problem isn't with the concept itself, but with the particular
implementation. As such, Rust does not have nulls, but it does have an enum
that can encode the concept of a value being present or absent. This enum is
`Option<T>`, and it is [defined by the standard library][option]<!-- ignore -->
as follows:
[option]: ../std/option/enum.Option.html
```rust
enum Option<T> {
Some(T),
None,
}
```
The `Option<T>` enum is so useful that it's even included in the prelude; you
don't need to import it explicitly. Furthermore, so are its variants: you can
use `Some` and `None` directly, without prefixing them with `Option::`.
`Option<T>` is still just a regular enum, however, and `Some(T)` and `None` are
still values of type `Option<T>`.
The `<T>` syntax is a feature of Rust we haven't talked about yet. It's a
generic type parameter, and we'll cover generics in more detail in Chapter 10.
For now, all you need to know is that this means the `Some` variant of the
`Option` enum can hold one piece of data of any type. Here are some examples of
using `Option` values to hold number types and string types:
```rust
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
```
If we use `None` rather than `Some`, we need to tell Rust what type of
`Option<T>` we have, because the compiler can't infer the type that the `Some`
variant will hold by looking at the `None` variant.
When we have a `Some` value, we know that there is a value present, and the
value is held within the `Some`. When we have a `None` value, in some sense,
that means the same thing that null does: we do not have a valid value. So why
is this any better than null?
In short, because `Option<T>` and `T` (where `T` can be any type) are different
types from each other, so the compiler won't let us use an `Option` value as if
it was definitely a valid value. For example, this code won't compile because
it's trying to compare an `Option<i8>` to an `i8`:
```rust,ignore
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
```
If we run this code, we get an error message like this:
```text
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is not satisfied
-->
|
7 | let sum = x + y;
| ^^^^^
|
```
Intense! What this error message is trying to say is that Rust does not
understand how to add an `Option<i8>` and an `i8`, since they're different
types. When we have a value of a type like `i8` in Rust, the compiler will
ensure that we always have a valid value. We can proceed confidently without
having to check for null before using that value. Only when we have an
`Option<i8>` (or whatever type of value we're working with) do we have to
worry about possibly not having a value, and the compiler will make sure we
handle that case before using the value.
In other words, you have to convert an `Option<T>` to a `T` before you can do
`T` stuff with it. This helps catch one of the most common issues with null,
generally: assuming that something isn't null when it actually is.
This is pretty powerful: in order to have a value that can possibly be null,
you have to explicitly opt in by making the type of that value `Option<T>`.
Then, when you use that value, you are required to explicitly handle the case
when the value is null. Everywhere that a value has a type that isn't an
`Option<T>`, you *can* safely assume that the value isn't null. This was a
deliberate design decision for Rust to limit null's pervasiveness and increase
the safety of Rust code.
So, how *do* you get the `T` value out of a `Some` variant when you have a
value of type `Option<T>` so that you can use that value? The `Option<T>` enum
has a large number of methods useful in a variety of situations that you can
check out in [its documentation][docs]<!-- ignore -->, and becoming familiar
with them will be extremely useful in your journey with Rust.
[docs]: ../std/option/enum.Option.html
What we generally want to do in order to use an `Option<T>` value is to have
code that will handle each variant. We want some code that will run only in the
case that we have a `Some(T)` value, and this code *is* allowed to use the
inner `T`. We want some *other* code to run if we have a `None` value, and that
code doesn't have a `T` value available. The `match` expression is a control
flow construct that does just this, when used with enums: it will run different
code depending on which variant of the enum it has, and that code can use the
data inside the matching value.