599 lines
44 KiB
HTML
599 lines
44 KiB
HTML
|
<!DOCTYPE HTML>
|
|||
|
<html lang="en" class="sidebar-visible no-js light">
|
|||
|
<head>
|
|||
|
<!-- Book generated using mdBook -->
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>Defining an Enum - The Rust Programming Language</title>
|
|||
|
|
|||
|
|
|||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|||
|
<meta name="description" content="">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|||
|
<meta name="theme-color" content="#ffffff" />
|
|||
|
|
|||
|
<link rel="shortcut icon" href="favicon.png">
|
|||
|
<link rel="stylesheet" href="css/variables.css">
|
|||
|
<link rel="stylesheet" href="css/general.css">
|
|||
|
<link rel="stylesheet" href="css/chrome.css">
|
|||
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|||
|
|
|||
|
<!-- Fonts -->
|
|||
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|||
|
<link href="googleFonts/css.css" rel="stylesheet" type="text/css">
|
|||
|
|
|||
|
<!-- Highlight.js Stylesheets -->
|
|||
|
<link rel="stylesheet" href="highlight.css">
|
|||
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|||
|
|
|||
|
<!-- Custom theme stylesheets -->
|
|||
|
|
|||
|
<link rel="stylesheet" href="ferris.css">
|
|||
|
|
|||
|
<link rel="stylesheet" href="theme/2018-edition.css">
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<!-- Provide site root to javascript -->
|
|||
|
<script type="text/javascript">
|
|||
|
var path_to_root = "";
|
|||
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|||
|
<script type="text/javascript">
|
|||
|
try {
|
|||
|
var theme = localStorage.getItem('mdbook-theme');
|
|||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|||
|
|
|||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|||
|
}
|
|||
|
|
|||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|||
|
}
|
|||
|
} catch (e) { }
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|||
|
<script type="text/javascript">
|
|||
|
var theme;
|
|||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|||
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|||
|
var html = document.querySelector('html');
|
|||
|
html.classList.remove('no-js')
|
|||
|
html.classList.remove('light')
|
|||
|
html.classList.add(theme);
|
|||
|
html.classList.add('js');
|
|||
|
</script>
|
|||
|
|
|||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|||
|
<script type="text/javascript">
|
|||
|
var html = document.querySelector('html');
|
|||
|
var sidebar = 'hidden';
|
|||
|
if (document.body.clientWidth >= 1080) {
|
|||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|||
|
sidebar = sidebar || 'visible';
|
|||
|
}
|
|||
|
html.classList.remove('sidebar-visible');
|
|||
|
html.classList.add("sidebar-" + sidebar);
|
|||
|
</script>
|
|||
|
|
|||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|||
|
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
|||
|
<ol class="chapter"><li class="expanded affix "><a href="title-page.html">The Rust Programming Language</a></li><li class="expanded affix "><a href="foreword.html">Foreword</a></li><li class="expanded affix "><a href="ch00-00-introduction.html">Introduction</a></li><li class="expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Installation</a></li><li class="expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Programming a Guessing Game</a></li><li class="expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Common Programming Concepts</a></li><li><ol class="section"><li class="expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Variables and Mutability</a></li><li class="expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Data Types</a></li><li class="expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Functions</a></li><li class="expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Comments</a></li><li class="expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Control Flow</a></li></ol></li><li class="expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Understanding Ownership</a></li><li><ol class="section"><li class="expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> What is Ownership?</a></li><li class="expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> References and Borrowing</a></li><li class="expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> The Slice Type</a></li></ol></li><li class="expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Using Structs to Structure Related Data</a></li><li><ol class="section"><li class="expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Defining and Instantiating Structs</a></li><li class="expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> An Example Program Using Structs</a></li><li class="expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Method Syntax</a></li></ol></li><li class="expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Enums and Pattern Matching</a></li><li><ol class="section"><li class="expanded "><a href="ch06-01-defining-an-enum.html" class="active"><strong aria-hidden="true">6.1.</strong> Defining an Enum</a></li><li class="expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> The match Control Flow Operator</a></li><li class="expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Concise Control Flow with if let</a></li></ol></li><li class="expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Managing Growing Projects with Packages, Crates, and Modules</a></li><li><ol class="section"><li class="expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Packages and Crates</a></li><li class="expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Defining Modules to Control Scope and Privacy</a></li><li class="expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-modu
|
|||
|
</div>
|
|||
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|||
|
</nav>
|
|||
|
|
|||
|
<div id="page-wrapper" class="page-wrapper">
|
|||
|
|
|||
|
<div class="page">
|
|||
|
|
|||
|
<div id="menu-bar" class="menu-bar">
|
|||
|
<div id="menu-bar-sticky-container">
|
|||
|
<div class="left-buttons">
|
|||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|||
|
<i class="fa fa-bars"></i>
|
|||
|
</button>
|
|||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|||
|
<i class="fa fa-paint-brush"></i>
|
|||
|
</button>
|
|||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|||
|
<i class="fa fa-search"></i>
|
|||
|
</button>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
<h1 class="menu-title">The Rust Programming Language</h1>
|
|||
|
|
|||
|
<div class="right-buttons">
|
|||
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|||
|
<i id="print-button" class="fa fa-print"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<div id="search-wrapper" class="hidden">
|
|||
|
<form id="searchbar-outer" class="searchbar-outer">
|
|||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|||
|
</form>
|
|||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|||
|
<ul id="searchresults">
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|||
|
<script type="text/javascript">
|
|||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|||
|
});
|
|||
|
</script>
|
|||
|
|
|||
|
<div id="content" class="content">
|
|||
|
<main>
|
|||
|
<h2><a class="header" href="#defining-an-enum" id="defining-an-enum">Defining an Enum</a></h2>
|
|||
|
<p>Let’s look at a situation we might want to express in code and see why enums
|
|||
|
are useful and more appropriate than structs in this case. Say we need to work
|
|||
|
with IP addresses. Currently, two major standards are used for IP addresses:
|
|||
|
version four and version six. These are the only possibilities for an IP
|
|||
|
address that our program will come across: we can <em>enumerate</em> all possible
|
|||
|
variants, which is where enumeration gets its name.</p>
|
|||
|
<p>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, because enum values can only be one of its variants.
|
|||
|
Both version four and version six addresses are still fundamentally IP
|
|||
|
addresses, so they should be treated as the same type when the code is handling
|
|||
|
situations that apply to any kind of IP address.</p>
|
|||
|
<p>We can express this concept in code by defining an <code>IpAddrKind</code> enumeration and
|
|||
|
listing the possible kinds an IP address can be, <code>V4</code> and <code>V6</code>. These are the
|
|||
|
variants of the enum:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum IpAddrKind {
|
|||
|
V4,
|
|||
|
V6,
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><code>IpAddrKind</code> is now a custom data type that we can use elsewhere in our code.</p>
|
|||
|
<h3><a class="header" href="#enum-values" id="enum-values">Enum Values</a></h3>
|
|||
|
<p>We can create instances of each of the two variants of <code>IpAddrKind</code> like this:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">enum IpAddrKind {
|
|||
|
</span><span class="boring"> V4,
|
|||
|
</span><span class="boring"> V6,
|
|||
|
</span><span class="boring">}
|
|||
|
</span><span class="boring">
|
|||
|
</span>let four = IpAddrKind::V4;
|
|||
|
let six = IpAddrKind::V6;
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>Note that the variants of the enum are namespaced under its identifier, and we
|
|||
|
use a double colon to separate the two. The reason this is useful is that now
|
|||
|
both values <code>IpAddrKind::V4</code> and <code>IpAddrKind::V6</code> are of the same type:
|
|||
|
<code>IpAddrKind</code>. We can then, for instance, define a function that takes any
|
|||
|
<code>IpAddrKind</code>:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">enum IpAddrKind {
|
|||
|
</span><span class="boring"> V4,
|
|||
|
</span><span class="boring"> V6,
|
|||
|
</span><span class="boring">}
|
|||
|
</span><span class="boring">
|
|||
|
</span>fn route(ip_kind: IpAddrKind) { }
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>And we can call this function with either variant:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">enum IpAddrKind {
|
|||
|
</span><span class="boring"> V4,
|
|||
|
</span><span class="boring"> V6,
|
|||
|
</span><span class="boring">}
|
|||
|
</span><span class="boring">
|
|||
|
</span><span class="boring">fn route(ip_kind: IpAddrKind) { }
|
|||
|
</span><span class="boring">
|
|||
|
</span>route(IpAddrKind::V4);
|
|||
|
route(IpAddrKind::V6);
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>Using enums has even more advantages. Thinking more about our IP address type,
|
|||
|
at the moment we don’t have a way to store the actual IP address <em>data</em>; we
|
|||
|
only know what <em>kind</em> it is. Given that you just learned about structs in
|
|||
|
Chapter 5, you might tackle this problem as shown in Listing 6-1.</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>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"),
|
|||
|
};
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 6-1: Storing the data and <code>IpAddrKind</code> variant of
|
|||
|
an IP address using a <code>struct</code></span></p>
|
|||
|
<p>Here, we’ve defined a struct <code>IpAddr</code> that has two fields: a <code>kind</code> field that
|
|||
|
is of type <code>IpAddrKind</code> (the enum we defined previously) and an <code>address</code> field
|
|||
|
of type <code>String</code>. We have two instances of this struct. The first, <code>home</code>, has
|
|||
|
the value <code>IpAddrKind::V4</code> as its <code>kind</code> with associated address data of
|
|||
|
<code>127.0.0.1</code>. The second instance, <code>loopback</code>, has the other variant of
|
|||
|
<code>IpAddrKind</code> as its <code>kind</code> value, <code>V6</code>, and has address <code>::1</code> associated with
|
|||
|
it. We’ve used a struct to bundle the <code>kind</code> and <code>address</code> values together, so
|
|||
|
now the variant is associated with the value.</p>
|
|||
|
<p>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 <code>IpAddr</code> enum says that both <code>V4</code> and <code>V6</code>
|
|||
|
variants will have associated <code>String</code> values:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum IpAddr {
|
|||
|
V4(String),
|
|||
|
V6(String),
|
|||
|
}
|
|||
|
|
|||
|
let home = IpAddr::V4(String::from("127.0.0.1"));
|
|||
|
|
|||
|
let loopback = IpAddr::V6(String::from("::1"));
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>We attach data to each variant of the enum directly, so there is no need for an
|
|||
|
extra struct.</p>
|
|||
|
<p>There’s another advantage to using an enum rather than a struct: each variant
|
|||
|
can have different types and amounts of associated 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 <code>V4</code> addresses as four <code>u8</code> values but
|
|||
|
still express <code>V6</code> addresses as one <code>String</code> value, we wouldn’t be able to with
|
|||
|
a struct. Enums handle this case with ease:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum IpAddr {
|
|||
|
V4(u8, u8, u8, u8),
|
|||
|
V6(String),
|
|||
|
}
|
|||
|
|
|||
|
let home = IpAddr::V4(127, 0, 0, 1);
|
|||
|
|
|||
|
let loopback = IpAddr::V6(String::from("::1"));
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>We’ve shown several different ways to define data structures to store version
|
|||
|
four and version six IP addresses. However, as it turns out, wanting to store
|
|||
|
IP addresses and encode which kind they are is so common that <a href="../std/net/enum.IpAddr.html">the standard
|
|||
|
library has a definition we can use!</a><!-- ignore --> Let’s look at how
|
|||
|
the standard library defines <code>IpAddr</code>: it has the exact enum and variants that
|
|||
|
we’ve defined and used, but it embeds the address data inside the variants in
|
|||
|
the form of two different structs, which are defined differently for each
|
|||
|
variant:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>struct Ipv4Addr {
|
|||
|
// --snip--
|
|||
|
}
|
|||
|
|
|||
|
struct Ipv6Addr {
|
|||
|
// --snip--
|
|||
|
}
|
|||
|
|
|||
|
enum IpAddr {
|
|||
|
V4(Ipv4Addr),
|
|||
|
V6(Ipv6Addr),
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>This code illustrates that you can put any kind of data inside an enum variant:
|
|||
|
strings, numeric types, or structs, for example. You can even include another
|
|||
|
enum! Also, standard library types are often not much more complicated than
|
|||
|
what you might come up with.</p>
|
|||
|
<p>Note that even though the standard library contains a definition for <code>IpAddr</code>,
|
|||
|
we can still create and use our own definition without conflict because we
|
|||
|
haven’t brought the standard library’s definition into our scope. We’ll talk
|
|||
|
more about bringing types into scope in Chapter 7.</p>
|
|||
|
<p>Let’s look at another example of an enum in Listing 6-2: this one has a wide
|
|||
|
variety of types embedded in its variants.</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum Message {
|
|||
|
Quit,
|
|||
|
Move { x: i32, y: i32 },
|
|||
|
Write(String),
|
|||
|
ChangeColor(i32, i32, i32),
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 6-2: A <code>Message</code> enum whose variants each store
|
|||
|
different amounts and types of values</span></p>
|
|||
|
<p>This enum has four variants with different types:</p>
|
|||
|
<ul>
|
|||
|
<li><code>Quit</code> has no data associated with it at all.</li>
|
|||
|
<li><code>Move</code> includes an anonymous struct inside it.</li>
|
|||
|
<li><code>Write</code> includes a single <code>String</code>.</li>
|
|||
|
<li><code>ChangeColor</code> includes three <code>i32</code> values.</li>
|
|||
|
</ul>
|
|||
|
<p>Defining an enum with variants such as the ones in Listing 6-2 is similar to
|
|||
|
defining different kinds of struct definitions, except the enum doesn’t use the
|
|||
|
<code>struct</code> keyword and all the variants are grouped together under the <code>Message</code>
|
|||
|
type. The following structs could hold the same data that the preceding enum
|
|||
|
variants hold:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>struct QuitMessage; // unit struct
|
|||
|
struct MoveMessage {
|
|||
|
x: i32,
|
|||
|
y: i32,
|
|||
|
}
|
|||
|
struct WriteMessage(String); // tuple struct
|
|||
|
struct ChangeColorMessage(i32, i32, i32); // tuple struct
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>But if we used the different structs, which each have their own type, we
|
|||
|
couldn’t as easily define a function to take any of these kinds of messages as
|
|||
|
we could with the <code>Message</code> enum defined in Listing 6-2, which is a single type.</p>
|
|||
|
<p>There is one more similarity between enums and structs: just as we’re able to
|
|||
|
define methods on structs using <code>impl</code>, we’re also able to define methods on
|
|||
|
enums. Here’s a method named <code>call</code> that we could define on our <code>Message</code> enum:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">enum Message {
|
|||
|
</span><span class="boring"> Quit,
|
|||
|
</span><span class="boring"> Move { x: i32, y: i32 },
|
|||
|
</span><span class="boring"> Write(String),
|
|||
|
</span><span class="boring"> ChangeColor(i32, i32, i32),
|
|||
|
</span><span class="boring">}
|
|||
|
</span><span class="boring">
|
|||
|
</span>impl Message {
|
|||
|
fn call(&self) {
|
|||
|
// method body would be defined here
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
let m = Message::Write(String::from("hello"));
|
|||
|
m.call();
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>The body of the method would use <code>self</code> to get the value that we called the
|
|||
|
method on. In this example, we’ve created a variable <code>m</code> that has the value
|
|||
|
<code>Message::Write(String::from("hello"))</code>, and that is what <code>self</code> will be in the
|
|||
|
body of the <code>call</code> method when <code>m.call()</code> runs.</p>
|
|||
|
<p>Let’s look at another enum in the standard library that is very common and
|
|||
|
useful: <code>Option</code>.</p>
|
|||
|
<h3><a class="header" href="#the-option-enum-and-its-advantages-over-null-values" id="the-option-enum-and-its-advantages-over-null-values">The <code>Option</code> Enum and Its Advantages Over Null Values</a></h3>
|
|||
|
<p>In the previous section, we looked at how the <code>IpAddr</code> enum let us use Rust’s
|
|||
|
type system to encode more information than just the data into our program.
|
|||
|
This section explores a case study of <code>Option</code>, which is another enum defined
|
|||
|
by the standard library. The <code>Option</code> 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 whether you’ve handled all the cases you should be handling;
|
|||
|
this functionality can prevent bugs that are extremely common in other
|
|||
|
programming languages.</p>
|
|||
|
<p>Programming language design is often thought of in terms of which features you
|
|||
|
include, but the features you exclude are important too. Rust doesn’t have the
|
|||
|
null feature that many other languages have. <em>Null</em> 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.</p>
|
|||
|
<p>In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony
|
|||
|
Hoare, the inventor of null, has this to say:</p>
|
|||
|
<blockquote>
|
|||
|
<p>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.</p>
|
|||
|
</blockquote>
|
|||
|
<p>The problem with null values is that if you try to use a null value as 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.</p>
|
|||
|
<p>However, the concept that null is trying to express is still a useful one: a
|
|||
|
null is a value that is currently invalid or absent for some reason.</p>
|
|||
|
<p>The problem isn’t really with the concept 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
|
|||
|
<code>Option<T></code>, and it is <a href="../std/option/enum.Option.html">defined by the standard library</a><!-- ignore -->
|
|||
|
as follows:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>enum Option<T> {
|
|||
|
Some(T),
|
|||
|
None,
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>The <code>Option<T></code> enum is so useful that it’s even included in the prelude; you
|
|||
|
don’t need to bring it into scope explicitly. In addition, so are its variants:
|
|||
|
you can use <code>Some</code> and <code>None</code> directly without the <code>Option::</code> prefix. The
|
|||
|
<code>Option<T></code> enum is still just a regular enum, and <code>Some(T)</code> and <code>None</code> are
|
|||
|
still variants of type <code>Option<T></code>.</p>
|
|||
|
<p>The <code><T></code> 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 <code><T></code> means the <code>Some</code> variant of the
|
|||
|
<code>Option</code> enum can hold one piece of data of any type. Here are some examples of
|
|||
|
using <code>Option</code> values to hold number types and string types:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>let some_number = Some(5);
|
|||
|
let some_string = Some("a string");
|
|||
|
|
|||
|
let absent_number: Option<i32> = None;
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>If we use <code>None</code> rather than <code>Some</code>, we need to tell Rust what type of
|
|||
|
<code>Option<T></code> we have, because the compiler can’t infer the type that the <code>Some</code>
|
|||
|
variant will hold by looking only at a <code>None</code> value.</p>
|
|||
|
<p>When we have a <code>Some</code> value, we know that a value is present and the value is
|
|||
|
held within the <code>Some</code>. When we have a <code>None</code> value, in some sense, it means
|
|||
|
the same thing as null: we don’t have a valid value. So why is having
|
|||
|
<code>Option<T></code> any better than having null?</p>
|
|||
|
<p>In short, because <code>Option<T></code> and <code>T</code> (where <code>T</code> can be any type) are different
|
|||
|
types, the compiler won’t let us use an <code>Option<T></code> value as if it were
|
|||
|
definitely a valid value. For example, this code won’t compile because it’s
|
|||
|
trying to add an <code>i8</code> to an <code>Option<i8></code>:</p>
|
|||
|
<pre><code class="language-rust ignore does_not_compile">let x: i8 = 5;
|
|||
|
let y: Option<i8> = Some(5);
|
|||
|
|
|||
|
let sum = x + y;
|
|||
|
</code></pre>
|
|||
|
<p>If we run this code, we get an error message like this:</p>
|
|||
|
<pre><code class="language-text">error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is
|
|||
|
not satisfied
|
|||
|
-->
|
|||
|
|
|
|||
|
5 | let sum = x + y;
|
|||
|
| ^ no implementation for `i8 + std::option::Option<i8>`
|
|||
|
|
|
|||
|
</code></pre>
|
|||
|
<p>Intense! In effect, this error message means that Rust doesn’t understand how
|
|||
|
to add an <code>i8</code> and an <code>Option<i8></code>, because they’re different types. When we
|
|||
|
have a value of a type like <code>i8</code> 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 <code>Option<i8></code> (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.</p>
|
|||
|
<p>In other words, you have to convert an <code>Option<T></code> to a <code>T</code> before you can
|
|||
|
perform <code>T</code> operations with it. Generally, this helps catch one of the most
|
|||
|
common issues with null: assuming that something isn’t null when it actually
|
|||
|
is.</p>
|
|||
|
<p>Not having to worry about incorrectly assuming a not-null value helps you to be
|
|||
|
more confident in your code. In order to have a value that can possibly be
|
|||
|
null, you must explicitly opt in by making the type of that value <code>Option<T></code>.
|
|||
|
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
|
|||
|
<code>Option<T></code>, you <em>can</em> 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.</p>
|
|||
|
<p>So, how do you get the <code>T</code> value out of a <code>Some</code> variant when you have a value
|
|||
|
of type <code>Option<T></code> so you can use that value? The <code>Option<T></code> enum has a large
|
|||
|
number of methods that are useful in a variety of situations; you can check
|
|||
|
them out in <a href="../std/option/enum.Option.html">its documentation</a><!-- ignore -->. Becoming familiar with
|
|||
|
the methods on <code>Option<T></code> will be extremely useful in your journey with Rust.</p>
|
|||
|
<p>In general, in order to use an <code>Option<T></code> value, you want to have code that
|
|||
|
will handle each variant. You want some code that will run only when you have a
|
|||
|
<code>Some(T)</code> value, and this code is allowed to use the inner <code>T</code>. You want some
|
|||
|
other code to run if you have a <code>None</code> value, and that code doesn’t have a <code>T</code>
|
|||
|
value available. The <code>match</code> 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.</p>
|
|||
|
|
|||
|
</main>
|
|||
|
|
|||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|||
|
<!-- Mobile navigation buttons -->
|
|||
|
|
|||
|
<a rel="prev" href="ch06-00-enums.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a rel="next" href="ch06-02-match.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
<div style="clear: both"></div>
|
|||
|
</nav>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|||
|
|
|||
|
<a href="ch06-00-enums.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|||
|
<i class="fa fa-angle-left"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<a href="ch06-02-match.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|||
|
<i class="fa fa-angle-right"></i>
|
|||
|
</a>
|
|||
|
|
|||
|
</nav>
|
|||
|
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script type="text/javascript">
|
|||
|
window.playpen_copyable = true;
|
|||
|
</script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
|
|||
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|||
|
|
|||
|
<!-- Custom JS scripts -->
|
|||
|
|
|||
|
<script type="text/javascript" src="ferris.js"></script>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</body>
|
|||
|
</html>
|