feat: added cleanscript
This commit is contained in:
30
ch08/ch08-00-common-collections.html
Normal file
30
ch08/ch08-00-common-collections.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Common Collections</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="common-collections"><a class="header" href="#common-collections">Common Collections</a></h1>
|
||||
<p>Rust’s standard library includes a number of very useful data structures called
|
||||
<em>collections</em>. Most other data types represent one specific value, but
|
||||
collections can contain multiple values. Unlike the built-in array and tuple
|
||||
types, the data that these collections point to is stored on the heap, which
|
||||
means the amount of data does not need to be known at compile time and can grow
|
||||
or shrink as the program runs. Each kind of collection has different
|
||||
capabilities and costs, and choosing an appropriate one for your current
|
||||
situation is a skill you’ll develop over time. In this chapter, we’ll discuss
|
||||
three collections that are used very often in Rust programs:</p>
|
||||
<ul>
|
||||
<li>A <em>vector</em> allows you to store a variable number of values next to each other.</li>
|
||||
<li>A <em>string</em> is a collection of characters. We’ve mentioned the <code>String</code> type
|
||||
previously, but in this chapter, we’ll talk about it in depth.</li>
|
||||
<li>A <em>hash map</em> allows you to associate a value with a specific key. It’s a
|
||||
particular implementation of the more general data structure called a <em>map</em>.</li>
|
||||
</ul>
|
||||
<p>To learn about the other kinds of collections provided by the standard library,
|
||||
see <a href="../std/collections/index.html">the documentation</a>.</p>
|
||||
<p>We’ll discuss how to create and update vectors, strings, and hash maps, as well
|
||||
as what makes each special.</p>
|
||||
</body>
|
||||
</html>
|
||||
267
ch08/ch08-01-vectors.html
Normal file
267
ch08/ch08-01-vectors.html
Normal file
@@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Storing Lists of Values with Vectors</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="storing-lists-of-values-with-vectors"><a class="header" href="#storing-lists-of-values-with-vectors">Storing Lists of Values with Vectors</a></h2>
|
||||
<p>The first collection type we’ll look at is <code>Vec<T></code>, also known as a vector.
|
||||
Vectors allow you to store more than one value in a single data structure that
|
||||
puts all the values next to each other in memory. Vectors can only store values
|
||||
of the same type. They are useful when you have a list of items, such as the
|
||||
lines of text in a file or the prices of items in a shopping cart.</p>
|
||||
<h3 id="creating-a-new-vector"><a class="header" href="#creating-a-new-vector">Creating a New Vector</a></h3>
|
||||
<p>To create a new, empty vector, we call the <code>Vec::new</code> function, as shown in
|
||||
Listing 8-1.</p>
|
||||
<figure class="listing" id="listing-8-1">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v: Vec<i32> = Vec::new();
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-1">Listing 8-1</a>: Creating a new, empty vector to hold values of type <code>i32</code></figcaption>
|
||||
</figure>
|
||||
<p>Note that we added a type annotation here. Because we aren’t inserting any
|
||||
values into this vector, Rust doesn’t know what kind of elements we intend to
|
||||
store. This is an important point. Vectors are implemented using generics;
|
||||
we’ll cover how to use generics with your own types in Chapter 10. For now,
|
||||
know that the <code>Vec<T></code> type provided by the standard library can hold any type.
|
||||
When we create a vector to hold a specific type, we can specify the type within
|
||||
angle brackets. In Listing 8-1, we’ve told Rust that the <code>Vec<T></code> in <code>v</code> will
|
||||
hold elements of the <code>i32</code> type.</p>
|
||||
<p>More often, you’ll create a <code>Vec<T></code> with initial values, and Rust will infer
|
||||
the type of value you want to store, so you rarely need to do this type
|
||||
annotation. Rust conveniently provides the <code>vec!</code> macro, which will create a
|
||||
new vector that holds the values you give it. Listing 8-2 creates a new
|
||||
<code>Vec<i32></code> that holds the values <code>1</code>, <code>2</code>, and <code>3</code>. The integer type is <code>i32</code>
|
||||
because that’s the default integer type, as we discussed in the <a href="../ch03/ch03-02-data-types.html#data-types">“Data
|
||||
Types”</a><!-- ignore --> section of Chapter 3.</p>
|
||||
<figure class="listing" id="listing-8-2">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v = vec![1, 2, 3];
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-2">Listing 8-2</a>: Creating a new vector containing values</figcaption>
|
||||
</figure>
|
||||
<p>Because we’ve given initial <code>i32</code> values, Rust can infer that the type of <code>v</code>
|
||||
is <code>Vec<i32></code>, and the type annotation isn’t necessary. Next, we’ll look at how
|
||||
to modify a vector.</p>
|
||||
<h3 id="updating-a-vector"><a class="header" href="#updating-a-vector">Updating a Vector</a></h3>
|
||||
<p>To create a vector and then add elements to it, we can use the <code>push</code> method,
|
||||
as shown in Listing 8-3.</p>
|
||||
<figure class="listing" id="listing-8-3">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut v = Vec::new();
|
||||
|
||||
v.push(5);
|
||||
v.push(6);
|
||||
v.push(7);
|
||||
v.push(8);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-3">Listing 8-3</a>: Using the <code>push</code> method to add values to a vector</figcaption>
|
||||
</figure>
|
||||
<p>As with any variable, if we want to be able to change its value, we need to
|
||||
make it mutable using the <code>mut</code> keyword, as discussed in Chapter 3. The numbers
|
||||
we place inside are all of type <code>i32</code>, and Rust infers this from the data, so
|
||||
we don’t need the <code>Vec<i32></code> annotation.</p>
|
||||
<h3 id="reading-elements-of-vectors"><a class="header" href="#reading-elements-of-vectors">Reading Elements of Vectors</a></h3>
|
||||
<p>There are two ways to reference a value stored in a vector: via indexing or by
|
||||
using the <code>get</code> method. In the following examples, we’ve annotated the types of
|
||||
the values that are returned from these functions for extra clarity.</p>
|
||||
<p>Listing 8-4 shows both methods of accessing a value in a vector, with indexing
|
||||
syntax and the <code>get</code> method.</p>
|
||||
<figure class="listing" id="listing-8-4">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
let third: &i32 = &v[2];
|
||||
println!("The third element is {third}");
|
||||
|
||||
let third: Option<&i32> = v.get(2);
|
||||
match third {
|
||||
Some(third) => println!("The third element is {third}"),
|
||||
None => println!("There is no third element."),
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-4">Listing 8-4</a>: Using indexing syntax and using the <code>get</code> method to access an item in a vector</figcaption>
|
||||
</figure>
|
||||
<p>Note a few details here. We use the index value of <code>2</code> to get the third element
|
||||
because vectors are indexed by number, starting at zero. Using <code>&</code> and <code>[]</code>
|
||||
gives us a reference to the element at the index value. When we use the <code>get</code>
|
||||
method with the index passed as an argument, we get an <code>Option<&T></code> that we can
|
||||
use with <code>match</code>.</p>
|
||||
<p>Rust provides these two ways to reference an element so that you can choose how
|
||||
the program behaves when you try to use an index value outside the range of
|
||||
existing elements. As an example, let’s see what happens when we have a vector
|
||||
of five elements and then we try to access an element at index 100 with each
|
||||
technique, as shown in Listing 8-5.</p>
|
||||
<figure class="listing" id="listing-8-5">
|
||||
<pre class="playground"><code class="language-rust should_panic panics edition2024"><span class="boring">fn main() {
|
||||
</span> let v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
let does_not_exist = &v[100];
|
||||
let does_not_exist = v.get(100);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-5">Listing 8-5</a>: Attempting to access the element at index 100 in a vector containing five elements</figcaption>
|
||||
</figure>
|
||||
<p>When we run this code, the first <code>[]</code> method will cause the program to panic
|
||||
because it references a nonexistent element. This method is best used when you
|
||||
want your program to crash if there’s an attempt to access an element past the
|
||||
end of the vector.</p>
|
||||
<p>When the <code>get</code> method is passed an index that is outside the vector, it returns
|
||||
<code>None</code> without panicking. You would use this method if accessing an element
|
||||
beyond the range of the vector may happen occasionally under normal
|
||||
circumstances. Your code will then have logic to handle having either
|
||||
<code>Some(&element)</code> or <code>None</code>, as discussed in Chapter 6. For example, the index
|
||||
could be coming from a person entering a number. If they accidentally enter a
|
||||
number that’s too large and the program gets a <code>None</code> value, you could tell the
|
||||
user how many items are in the current vector and give them another chance to
|
||||
enter a valid value. That would be more user-friendly than crashing the program
|
||||
due to a typo!</p>
|
||||
<p>When the program has a valid reference, the borrow checker enforces the
|
||||
ownership and borrowing rules (covered in Chapter 4) to ensure that this
|
||||
reference and any other references to the contents of the vector remain valid.
|
||||
Recall the rule that states you can’t have mutable and immutable references in
|
||||
the same scope. That rule applies in Listing 8-6, where we hold an immutable
|
||||
reference to the first element in a vector and try to add an element to the
|
||||
end. This program won’t work if we also try to refer to that element later in
|
||||
the function.</p>
|
||||
<figure class="listing" id="listing-8-6">
|
||||
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
|
||||
</span> let mut v = vec![1, 2, 3, 4, 5];
|
||||
|
||||
let first = &v[0];
|
||||
|
||||
v.push(6);
|
||||
|
||||
println!("The first element is: {first}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-6">Listing 8-6</a>: Attempting to add an element to a vector while holding a reference to an item</figcaption>
|
||||
</figure>
|
||||
<p>Compiling this code will result in this error:</p>
|
||||
<pre><code class="language-console">$ cargo run
|
||||
Compiling collections v0.1.0 (file:///projects/collections)
|
||||
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
|
||||
--> src/main.rs:6:5
|
||||
|
|
||||
4 | let first = &v[0];
|
||||
| - immutable borrow occurs here
|
||||
5 |
|
||||
6 | v.push(6);
|
||||
| ^^^^^^^^^ mutable borrow occurs here
|
||||
7 |
|
||||
8 | println!("The first element is: {first}");
|
||||
| ----- immutable borrow later used here
|
||||
|
||||
For more information about this error, try `rustc --explain E0502`.
|
||||
error: could not compile `collections` (bin "collections") due to 1 previous error
|
||||
</code></pre>
|
||||
<p>The code in Listing 8-6 might look like it should work: Why should a reference
|
||||
to the first element care about changes at the end of the vector? This error is
|
||||
due to the way vectors work: Because vectors put the values next to each other
|
||||
in memory, adding a new element onto the end of the vector might require
|
||||
allocating new memory and copying the old elements to the new space, if there
|
||||
isn’t enough room to put all the elements next to each other where the vector
|
||||
is currently stored. In that case, the reference to the first element would be
|
||||
pointing to deallocated memory. The borrowing rules prevent programs from
|
||||
ending up in that situation.</p>
|
||||
<section class="note" aria-role="note">
|
||||
<p>Note: For more on the implementation details of the <code>Vec<T></code> type, see <a href="../nomicon/vec/vec.html">“The
|
||||
Rustonomicon”</a>.</p>
|
||||
</section>
|
||||
<h3 id="iterating-over-the-values-in-a-vector"><a class="header" href="#iterating-over-the-values-in-a-vector">Iterating Over the Values in a Vector</a></h3>
|
||||
<p>To access each element in a vector in turn, we would iterate through all of the
|
||||
elements rather than use indices to access one at a time. Listing 8-7 shows how
|
||||
to use a <code>for</code> loop to get immutable references to each element in a vector of
|
||||
<code>i32</code> values and print them.</p>
|
||||
<figure class="listing" id="listing-8-7">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v = vec![100, 32, 57];
|
||||
for i in &v {
|
||||
println!("{i}");
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-7">Listing 8-7</a>: Printing each element in a vector by iterating over the elements using a <code>for</code> loop</figcaption>
|
||||
</figure>
|
||||
<p>We can also iterate over mutable references to each element in a mutable vector
|
||||
in order to make changes to all the elements. The <code>for</code> loop in Listing 8-8
|
||||
will add <code>50</code> to each element.</p>
|
||||
<figure class="listing" id="listing-8-8">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut v = vec![100, 32, 57];
|
||||
for i in &mut v {
|
||||
*i += 50;
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-8">Listing 8-8</a>: Iterating over mutable references to elements in a vector</figcaption>
|
||||
</figure>
|
||||
<p>To change the value that the mutable reference refers to, we have to use the
|
||||
<code>*</code> dereference operator to get to the value in <code>i</code> before we can use the <code>+=</code>
|
||||
operator. We’ll talk more about the dereference operator in the <a href="../ch15/ch15-02-deref.html#following-the-pointer-to-the-value-with-the-dereference-operator">“Following the
|
||||
Reference to the Value”</a><!-- ignore --> section of Chapter 15.</p>
|
||||
<p>Iterating over a vector, whether immutably or mutably, is safe because of the
|
||||
borrow checker’s rules. If we attempted to insert or remove items in the <code>for</code>
|
||||
loop bodies in Listing 8-7 and Listing 8-8, we would get a compiler error
|
||||
similar to the one we got with the code in Listing 8-6. The reference to the
|
||||
vector that the <code>for</code> loop holds prevents simultaneous modification of the
|
||||
whole vector.</p>
|
||||
<h3 id="using-an-enum-to-store-multiple-types"><a class="header" href="#using-an-enum-to-store-multiple-types">Using an Enum to Store Multiple Types</a></h3>
|
||||
<p>Vectors can only store values that are of the same type. This can be
|
||||
inconvenient; there are definitely use cases for needing to store a list of
|
||||
items of different types. Fortunately, the variants of an enum are defined
|
||||
under the same enum type, so when we need one type to represent elements of
|
||||
different types, we can define and use an enum!</p>
|
||||
<p>For example, say we want to get values from a row in a spreadsheet in which
|
||||
some of the columns in the row contain integers, some floating-point numbers,
|
||||
and some strings. We can define an enum whose variants will hold the different
|
||||
value types, and all the enum variants will be considered the same type: that
|
||||
of the enum. Then, we can create a vector to hold that enum and so, ultimately,
|
||||
hold different types. We’ve demonstrated this in Listing 8-9.</p>
|
||||
<figure class="listing" id="listing-8-9">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> enum SpreadsheetCell {
|
||||
Int(i32),
|
||||
Float(f64),
|
||||
Text(String),
|
||||
}
|
||||
|
||||
let row = vec![
|
||||
SpreadsheetCell::Int(3),
|
||||
SpreadsheetCell::Text(String::from("blue")),
|
||||
SpreadsheetCell::Float(10.12),
|
||||
];
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-9">Listing 8-9</a>: Defining an enum to store values of different types in one vector</figcaption>
|
||||
</figure>
|
||||
<p>Rust needs to know what types will be in the vector at compile time so that it
|
||||
knows exactly how much memory on the heap will be needed to store each element.
|
||||
We must also be explicit about what types are allowed in this vector. If Rust
|
||||
allowed a vector to hold any type, there would be a chance that one or more of
|
||||
the types would cause errors with the operations performed on the elements of
|
||||
the vector. Using an enum plus a <code>match</code> expression means that Rust will ensure
|
||||
at compile time that every possible case is handled, as discussed in Chapter 6.</p>
|
||||
<p>If you don’t know the exhaustive set of types a program will get at runtime to
|
||||
store in a vector, the enum technique won’t work. Instead, you can use a trait
|
||||
object, which we’ll cover in Chapter 18.</p>
|
||||
<p>Now that we’ve discussed some of the most common ways to use vectors, be sure
|
||||
to review <a href="../std/vec/struct.Vec.html">the API documentation</a><!-- ignore --> for all of the many
|
||||
useful methods defined on <code>Vec<T></code> by the standard library. For example, in
|
||||
addition to <code>push</code>, a <code>pop</code> method removes and returns the last element.</p>
|
||||
<h3 id="dropping-a-vector-drops-its-elements"><a class="header" href="#dropping-a-vector-drops-its-elements">Dropping a Vector Drops Its Elements</a></h3>
|
||||
<p>Like any other <code>struct</code>, a vector is freed when it goes out of scope, as
|
||||
annotated in Listing 8-10.</p>
|
||||
<figure class="listing" id="listing-8-10">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> {
|
||||
let v = vec![1, 2, 3, 4];
|
||||
|
||||
// do stuff with v
|
||||
} // <- v goes out of scope and is freed here
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-10">Listing 8-10</a>: Showing where the vector and its elements are dropped</figcaption>
|
||||
</figure>
|
||||
<p>When the vector gets dropped, all of its contents are also dropped, meaning the
|
||||
integers it holds will be cleaned up. The borrow checker ensures that any
|
||||
references to contents of a vector are only used while the vector itself is
|
||||
valid.</p>
|
||||
<p>Let’s move on to the next collection type: <code>String</code>!</p>
|
||||
</body>
|
||||
</html>
|
||||
412
ch08/ch08-02-strings.html
Normal file
412
ch08/ch08-02-strings.html
Normal file
@@ -0,0 +1,412 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Storing UTF-8 Encoded Text with Strings</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="storing-utf-8-encoded-text-with-strings"><a class="header" href="#storing-utf-8-encoded-text-with-strings">Storing UTF-8 Encoded Text with Strings</a></h2>
|
||||
<p>We talked about strings in Chapter 4, but we’ll look at them in more depth now.
|
||||
New Rustaceans commonly get stuck on strings for a combination of three
|
||||
reasons: Rust’s propensity for exposing possible errors, strings being a more
|
||||
complicated data structure than many programmers give them credit for, and
|
||||
UTF-8. These factors combine in a way that can seem difficult when you’re
|
||||
coming from other programming languages.</p>
|
||||
<p>We discuss strings in the context of collections because strings are
|
||||
implemented as a collection of bytes, plus some methods to provide useful
|
||||
functionality when those bytes are interpreted as text. In this section, we’ll
|
||||
talk about the operations on <code>String</code> that every collection type has, such as
|
||||
creating, updating, and reading. We’ll also discuss the ways in which <code>String</code>
|
||||
is different from the other collections, namely, how indexing into a <code>String</code> is
|
||||
complicated by the differences between how people and computers interpret
|
||||
<code>String</code> data.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="what-is-a-string"></a></p>
|
||||
<h3 id="defining-strings"><a class="header" href="#defining-strings">Defining Strings</a></h3>
|
||||
<p>We’ll first define what we mean by the term <em>string</em>. Rust has only one string
|
||||
type in the core language, which is the string slice <code>str</code> that is usually seen
|
||||
in its borrowed form, <code>&str</code>. In Chapter 4, we talked about string slices,
|
||||
which are references to some UTF-8 encoded string data stored elsewhere. String
|
||||
literals, for example, are stored in the program’s binary and are therefore
|
||||
string slices.</p>
|
||||
<p>The <code>String</code> type, which is provided by Rust’s standard library rather than
|
||||
coded into the core language, is a growable, mutable, owned, UTF-8 encoded
|
||||
string type. When Rustaceans refer to “strings” in Rust, they might be
|
||||
referring to either the <code>String</code> or the string slice <code>&str</code> types, not just one
|
||||
of those types. Although this section is largely about <code>String</code>, both types are
|
||||
used heavily in Rust’s standard library, and both <code>String</code> and string slices
|
||||
are UTF-8 encoded.</p>
|
||||
<h3 id="creating-a-new-string"><a class="header" href="#creating-a-new-string">Creating a New String</a></h3>
|
||||
<p>Many of the same operations available with <code>Vec<T></code> are available with <code>String</code>
|
||||
as well because <code>String</code> is actually implemented as a wrapper around a vector
|
||||
of bytes with some extra guarantees, restrictions, and capabilities. An example
|
||||
of a function that works the same way with <code>Vec<T></code> and <code>String</code> is the <code>new</code>
|
||||
function to create an instance, shown in Listing 8-11.</p>
|
||||
<figure class="listing" id="listing-8-11">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut s = String::new();
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-11">Listing 8-11</a>: Creating a new, empty <code>String</code></figcaption>
|
||||
</figure>
|
||||
<p>This line creates a new, empty string called <code>s</code>, into which we can then load
|
||||
data. Often, we’ll have some initial data with which we want to start the
|
||||
string. For that, we use the <code>to_string</code> method, which is available on any type
|
||||
that implements the <code>Display</code> trait, as string literals do. Listing 8-12 shows
|
||||
two examples.</p>
|
||||
<figure class="listing" id="listing-8-12">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let data = "initial contents";
|
||||
|
||||
let s = data.to_string();
|
||||
|
||||
// The method also works on a literal directly:
|
||||
let s = "initial contents".to_string();
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-12">Listing 8-12</a>: Using the <code>to_string</code> method to create a <code>String</code> from a string literal</figcaption>
|
||||
</figure>
|
||||
<p>This code creates a string containing <code>initial contents</code>.</p>
|
||||
<p>We can also use the function <code>String::from</code> to create a <code>String</code> from a string
|
||||
literal. The code in Listing 8-13 is equivalent to the code in Listing 8-12
|
||||
that uses <code>to_string</code>.</p>
|
||||
<figure class="listing" id="listing-8-13">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let s = String::from("initial contents");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-13">Listing 8-13</a>: Using the <code>String::from</code> function to create a <code>String</code> from a string literal</figcaption>
|
||||
</figure>
|
||||
<p>Because strings are used for so many things, we can use many different generic
|
||||
APIs for strings, providing us with a lot of options. Some of them can seem
|
||||
redundant, but they all have their place! In this case, <code>String::from</code> and
|
||||
<code>to_string</code> do the same thing, so which one you choose is a matter of style and
|
||||
readability.</p>
|
||||
<p>Remember that strings are UTF-8 encoded, so we can include any properly encoded
|
||||
data in them, as shown in Listing 8-14.</p>
|
||||
<figure class="listing" id="listing-8-14">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let hello = String::from("السلام عليكم");
|
||||
let hello = String::from("Dobrý den");
|
||||
let hello = String::from("Hello");
|
||||
let hello = String::from("שלום");
|
||||
let hello = String::from("नमस्ते");
|
||||
let hello = String::from("こんにちは");
|
||||
let hello = String::from("안녕하세요");
|
||||
let hello = String::from("你好");
|
||||
let hello = String::from("Olá");
|
||||
let hello = String::from("Здравствуйте");
|
||||
let hello = String::from("Hola");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-14">Listing 8-14</a>: Storing greetings in different languages in strings</figcaption>
|
||||
</figure>
|
||||
<p>All of these are valid <code>String</code> values.</p>
|
||||
<h3 id="updating-a-string"><a class="header" href="#updating-a-string">Updating a String</a></h3>
|
||||
<p>A <code>String</code> can grow in size and its contents can change, just like the contents
|
||||
of a <code>Vec<T></code>, if you push more data into it. In addition, you can conveniently
|
||||
use the <code>+</code> operator or the <code>format!</code> macro to concatenate <code>String</code> values.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="appending-to-a-string-with-push_str-and-push"></a></p>
|
||||
<h4 id="appending-with-push_str-or-push"><a class="header" href="#appending-with-push_str-or-push">Appending with <code>push_str</code> or <code>push</code></a></h4>
|
||||
<p>We can grow a <code>String</code> by using the <code>push_str</code> method to append a string slice,
|
||||
as shown in Listing 8-15.</p>
|
||||
<figure class="listing" id="listing-8-15">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut s = String::from("foo");
|
||||
s.push_str("bar");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-15">Listing 8-15</a>: Appending a string slice to a <code>String</code> using the <code>push_str</code> method</figcaption>
|
||||
</figure>
|
||||
<p>After these two lines, <code>s</code> will contain <code>foobar</code>. The <code>push_str</code> method takes a
|
||||
string slice because we don’t necessarily want to take ownership of the
|
||||
parameter. For example, in the code in Listing 8-16, we want to be able to use
|
||||
<code>s2</code> after appending its contents to <code>s1</code>.</p>
|
||||
<figure class="listing" id="listing-8-16">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut s1 = String::from("foo");
|
||||
let s2 = "bar";
|
||||
s1.push_str(s2);
|
||||
println!("s2 is {s2}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-16">Listing 8-16</a>: Using a string slice after appending its contents to a <code>String</code></figcaption>
|
||||
</figure>
|
||||
<p>If the <code>push_str</code> method took ownership of <code>s2</code>, we wouldn’t be able to print
|
||||
its value on the last line. However, this code works as we’d expect!</p>
|
||||
<p>The <code>push</code> method takes a single character as a parameter and adds it to the
|
||||
<code>String</code>. Listing 8-17 adds the letter <em>l</em> to a <code>String</code> using the <code>push</code>
|
||||
method.</p>
|
||||
<figure class="listing" id="listing-8-17">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let mut s = String::from("lo");
|
||||
s.push('l');
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-17">Listing 8-17</a>: Adding one character to a <code>String</code> value using <code>push</code></figcaption>
|
||||
</figure>
|
||||
<p>As a result, <code>s</code> will contain <code>lol</code>.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="concatenation-with-the--operator-or-the-format-macro"></a></p>
|
||||
<h4 id="concatenating-with--or-format"><a class="header" href="#concatenating-with--or-format">Concatenating with <code>+</code> or <code>format!</code></a></h4>
|
||||
<p>Often, you’ll want to combine two existing strings. One way to do so is to use
|
||||
the <code>+</code> operator, as shown in Listing 8-18.</p>
|
||||
<figure class="listing" id="listing-8-18">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let s1 = String::from("Hello, ");
|
||||
let s2 = String::from("world!");
|
||||
let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-18">Listing 8-18</a>: Using the <code>+</code> operator to combine two <code>String</code> values into a new <code>String</code> value</figcaption>
|
||||
</figure>
|
||||
<p>The string <code>s3</code> will contain <code>Hello, world!</code>. The reason <code>s1</code> is no longer
|
||||
valid after the addition, and the reason we used a reference to <code>s2</code>, has to do
|
||||
with the signature of the method that’s called when we use the <code>+</code> operator.
|
||||
The <code>+</code> operator uses the <code>add</code> method, whose signature looks something like
|
||||
this:</p>
|
||||
<pre><code class="language-rust ignore">fn add(self, s: &str) -> String {</code></pre>
|
||||
<p>In the standard library, you’ll see <code>add</code> defined using generics and associated
|
||||
types. Here, we’ve substituted in concrete types, which is what happens when we
|
||||
call this method with <code>String</code> values. We’ll discuss generics in Chapter 10.
|
||||
This signature gives us the clues we need in order to understand the tricky
|
||||
bits of the <code>+</code> operator.</p>
|
||||
<p>First, <code>s2</code> has an <code>&</code>, meaning that we’re adding a reference of the second
|
||||
string to the first string. This is because of the <code>s</code> parameter in the <code>add</code>
|
||||
function: We can only add a string slice to a <code>String</code>; we can’t add two
|
||||
<code>String</code> values together. But wait—the type of <code>&s2</code> is <code>&String</code>, not <code>&str</code>,
|
||||
as specified in the second parameter to <code>add</code>. So, why does Listing 8-18
|
||||
compile?</p>
|
||||
<p>The reason we’re able to use <code>&s2</code> in the call to <code>add</code> is that the compiler
|
||||
can coerce the <code>&String</code> argument into a <code>&str</code>. When we call the <code>add</code> method,
|
||||
Rust uses a deref coercion, which here turns <code>&s2</code> into <code>&s2[..]</code>. We’ll
|
||||
discuss deref coercion in more depth in Chapter 15. Because <code>add</code> does not take
|
||||
ownership of the <code>s</code> parameter, <code>s2</code> will still be a valid <code>String</code> after this
|
||||
operation.</p>
|
||||
<p>Second, we can see in the signature that <code>add</code> takes ownership of <code>self</code>
|
||||
because <code>self</code> does <em>not</em> have an <code>&</code>. This means <code>s1</code> in Listing 8-18 will be
|
||||
moved into the <code>add</code> call and will no longer be valid after that. So, although
|
||||
<code>let s3 = s1 + &s2;</code> looks like it will copy both strings and create a new one,
|
||||
this statement actually takes ownership of <code>s1</code>, appends a copy of the contents
|
||||
of <code>s2</code>, and then returns ownership of the result. In other words, it looks
|
||||
like it’s making a lot of copies, but it isn’t; the implementation is more
|
||||
efficient than copying.</p>
|
||||
<p>If we need to concatenate multiple strings, the behavior of the <code>+</code> operator
|
||||
gets unwieldy:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let s1 = String::from("tic");
|
||||
let s2 = String::from("tac");
|
||||
let s3 = String::from("toe");
|
||||
|
||||
let s = s1 + "-" + &s2 + "-" + &s3;
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>At this point, <code>s</code> will be <code>tic-tac-toe</code>. With all of the <code>+</code> and <code>"</code>
|
||||
characters, it’s difficult to see what’s going on. For combining strings in
|
||||
more complicated ways, we can instead use the <code>format!</code> macro:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let s1 = String::from("tic");
|
||||
let s2 = String::from("tac");
|
||||
let s3 = String::from("toe");
|
||||
|
||||
let s = format!("{s1}-{s2}-{s3}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>This code also sets <code>s</code> to <code>tic-tac-toe</code>. The <code>format!</code> macro works like
|
||||
<code>println!</code>, but instead of printing the output to the screen, it returns a
|
||||
<code>String</code> with the contents. The version of the code using <code>format!</code> is much
|
||||
easier to read, and the code generated by the <code>format!</code> macro uses references
|
||||
so that this call doesn’t take ownership of any of its parameters.</p>
|
||||
<h3 id="indexing-into-strings"><a class="header" href="#indexing-into-strings">Indexing into Strings</a></h3>
|
||||
<p>In many other programming languages, accessing individual characters in a
|
||||
string by referencing them by index is a valid and common operation. However,
|
||||
if you try to access parts of a <code>String</code> using indexing syntax in Rust, you’ll
|
||||
get an error. Consider the invalid code in Listing 8-19.</p>
|
||||
<figure class="listing" id="listing-8-19">
|
||||
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
|
||||
</span> let s1 = String::from("hi");
|
||||
let h = s1[0];
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-19">Listing 8-19</a>: Attempting to use indexing syntax with a <code>String</code></figcaption>
|
||||
</figure>
|
||||
<p>This code will result in the following error:</p>
|
||||
<pre><code class="language-console">$ cargo run
|
||||
Compiling collections v0.1.0 (file:///projects/collections)
|
||||
error[E0277]: the type `str` cannot be indexed by `{integer}`
|
||||
--> src/main.rs:3:16
|
||||
|
|
||||
3 | let h = s1[0];
|
||||
| ^ string indices are ranges of `usize`
|
||||
|
|
||||
= help: the trait `SliceIndex<str>` is not implemented for `{integer}`
|
||||
= note: you can use `.chars().nth()` or `.bytes().nth()`
|
||||
for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
|
||||
= help: the following other types implement trait `SliceIndex<T>`:
|
||||
`usize` implements `SliceIndex<ByteStr>`
|
||||
`usize` implements `SliceIndex<[T]>`
|
||||
= note: required for `String` to implement `Index<{integer}>`
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
error: could not compile `collections` (bin "collections") due to 1 previous error
|
||||
</code></pre>
|
||||
<p>The error tells the story: Rust strings don’t support indexing. But why not? To
|
||||
answer that question, we need to discuss how Rust stores strings in memory.</p>
|
||||
<h4 id="internal-representation"><a class="header" href="#internal-representation">Internal Representation</a></h4>
|
||||
<p>A <code>String</code> is a wrapper over a <code>Vec<u8></code>. Let’s look at some of our properly
|
||||
encoded UTF-8 example strings from Listing 8-14. First, this one:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span><span class="boring"> let hello = String::from("السلام عليكم");
|
||||
</span><span class="boring"> let hello = String::from("Dobrý den");
|
||||
</span><span class="boring"> let hello = String::from("Hello");
|
||||
</span><span class="boring"> let hello = String::from("שלום");
|
||||
</span><span class="boring"> let hello = String::from("नमस्ते");
|
||||
</span><span class="boring"> let hello = String::from("こんにちは");
|
||||
</span><span class="boring"> let hello = String::from("안녕하세요");
|
||||
</span><span class="boring"> let hello = String::from("你好");
|
||||
</span><span class="boring"> let hello = String::from("Olá");
|
||||
</span><span class="boring"> let hello = String::from("Здравствуйте");
|
||||
</span> let hello = String::from("Hola");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>In this case, <code>len</code> will be <code>4</code>, which means the vector storing the string
|
||||
<code>"Hola"</code> is 4 bytes long. Each of these letters takes 1 byte when encoded in
|
||||
UTF-8. The following line, however, may surprise you (note that this string
|
||||
begins with the capital Cyrillic letter <em>Ze</em>, not the number 3):</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span><span class="boring"> let hello = String::from("السلام عليكم");
|
||||
</span><span class="boring"> let hello = String::from("Dobrý den");
|
||||
</span><span class="boring"> let hello = String::from("Hello");
|
||||
</span><span class="boring"> let hello = String::from("שלום");
|
||||
</span><span class="boring"> let hello = String::from("नमस्ते");
|
||||
</span><span class="boring"> let hello = String::from("こんにちは");
|
||||
</span><span class="boring"> let hello = String::from("안녕하세요");
|
||||
</span><span class="boring"> let hello = String::from("你好");
|
||||
</span><span class="boring"> let hello = String::from("Olá");
|
||||
</span> let hello = String::from("Здравствуйте");
|
||||
<span class="boring"> let hello = String::from("Hola");
|
||||
</span><span class="boring">}</span></code></pre>
|
||||
<p>If you were asked how long the string is, you might say 12. In fact, Rust’s
|
||||
answer is 24: That’s the number of bytes it takes to encode “Здравствуйте” in
|
||||
UTF-8, because each Unicode scalar value in that string takes 2 bytes of
|
||||
storage. Therefore, an index into the string’s bytes will not always correlate
|
||||
to a valid Unicode scalar value. To demonstrate, consider this invalid Rust
|
||||
code:</p>
|
||||
<pre><code class="language-rust ignore does_not_compile">let hello = "Здравствуйте";
|
||||
let answer = &hello[0];</code></pre>
|
||||
<p>You already know that <code>answer</code> will not be <code>З</code>, the first letter. When encoded
|
||||
in UTF-8, the first byte of <code>З</code> is <code>208</code> and the second is <code>151</code>, so it would
|
||||
seem that <code>answer</code> should in fact be <code>208</code>, but <code>208</code> is not a valid character
|
||||
on its own. Returning <code>208</code> is likely not what a user would want if they asked
|
||||
for the first letter of this string; however, that’s the only data that Rust
|
||||
has at byte index 0. Users generally don’t want the byte value returned, even
|
||||
if the string contains only Latin letters: If <code>&"hi"[0]</code> were valid code that
|
||||
returned the byte value, it would return <code>104</code>, not <code>h</code>.</p>
|
||||
<p>The answer, then, is that to avoid returning an unexpected value and causing
|
||||
bugs that might not be discovered immediately, Rust doesn’t compile this code
|
||||
at all and prevents misunderstandings early in the development process.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="bytes-and-scalar-values-and-grapheme-clusters-oh-my"></a></p>
|
||||
<h4 id="bytes-scalar-values-and-grapheme-clusters"><a class="header" href="#bytes-scalar-values-and-grapheme-clusters">Bytes, Scalar Values, and Grapheme Clusters</a></h4>
|
||||
<p>Another point about UTF-8 is that there are actually three relevant ways to
|
||||
look at strings from Rust’s perspective: as bytes, scalar values, and grapheme
|
||||
clusters (the closest thing to what we would call <em>letters</em>).</p>
|
||||
<p>If we look at the Hindi word “नमस्ते” written in the Devanagari script, it is
|
||||
stored as a vector of <code>u8</code> values that looks like this:</p>
|
||||
<pre><code class="language-text">[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
|
||||
224, 165, 135]
|
||||
</code></pre>
|
||||
<p>That’s 18 bytes and is how computers ultimately store this data. If we look at
|
||||
them as Unicode scalar values, which are what Rust’s <code>char</code> type is, those
|
||||
bytes look like this:</p>
|
||||
<pre><code class="language-text">['न', 'म', 'स', '्', 'त', 'े']
|
||||
</code></pre>
|
||||
<p>There are six <code>char</code> values here, but the fourth and sixth are not letters:
|
||||
They’re diacritics that don’t make sense on their own. Finally, if we look at
|
||||
them as grapheme clusters, we’d get what a person would call the four letters
|
||||
that make up the Hindi word:</p>
|
||||
<pre><code class="language-text">["न", "म", "स्", "ते"]
|
||||
</code></pre>
|
||||
<p>Rust provides different ways of interpreting the raw string data that computers
|
||||
store so that each program can choose the interpretation it needs, no matter
|
||||
what human language the data is in.</p>
|
||||
<p>A final reason Rust doesn’t allow us to index into a <code>String</code> to get a
|
||||
character is that indexing operations are expected to always take constant time
|
||||
(O(1)). But it isn’t possible to guarantee that performance with a <code>String</code>,
|
||||
because Rust would have to walk through the contents from the beginning to the
|
||||
index to determine how many valid characters there were.</p>
|
||||
<h3 id="slicing-strings"><a class="header" href="#slicing-strings">Slicing Strings</a></h3>
|
||||
<p>Indexing into a string is often a bad idea because it’s not clear what the
|
||||
return type of the string-indexing operation should be: a byte value, a
|
||||
character, a grapheme cluster, or a string slice. If you really need to use
|
||||
indices to create string slices, therefore, Rust asks you to be more specific.</p>
|
||||
<p>Rather than indexing using <code>[]</code> with a single number, you can use <code>[]</code> with a
|
||||
range to create a string slice containing particular bytes:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>let hello = "Здравствуйте";
|
||||
|
||||
let s = &hello[0..4];
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>Here, <code>s</code> will be a <code>&str</code> that contains the first 4 bytes of the string.
|
||||
Earlier, we mentioned that each of these characters was 2 bytes, which means
|
||||
<code>s</code> will be <code>Зд</code>.</p>
|
||||
<p>If we were to try to slice only part of a character’s bytes with something like
|
||||
<code>&hello[0..1]</code>, Rust would panic at runtime in the same way as if an invalid
|
||||
index were accessed in a vector:</p>
|
||||
<pre><code class="language-console">$ cargo run
|
||||
Compiling collections v0.1.0 (file:///projects/collections)
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.43s
|
||||
Running `target/debug/collections`
|
||||
|
||||
thread 'main' panicked at src/main.rs:4:19:
|
||||
byte index 1 is not a char boundary; it is inside 'З' (bytes 0..2) of `Здравствуйте`
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
</code></pre>
|
||||
<p>You should use caution when creating string slices with ranges, because doing
|
||||
so can crash your program.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="methods-for-iterating-over-strings"></a></p>
|
||||
<h3 id="iterating-over-strings"><a class="header" href="#iterating-over-strings">Iterating Over Strings</a></h3>
|
||||
<p>The best way to operate on pieces of strings is to be explicit about whether
|
||||
you want characters or bytes. For individual Unicode scalar values, use the
|
||||
<code>chars</code> method. Calling <code>chars</code> on “Зд” separates out and returns two values of
|
||||
type <code>char</code>, and you can iterate over the result to access each element:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>for c in "Зд".chars() {
|
||||
println!("{c}");
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>This code will print the following:</p>
|
||||
<pre><code class="language-text">З
|
||||
д
|
||||
</code></pre>
|
||||
<p>Alternatively, the <code>bytes</code> method returns each raw byte, which might be
|
||||
appropriate for your domain:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>for b in "Зд".bytes() {
|
||||
println!("{b}");
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>This code will print the 4 bytes that make up this string:</p>
|
||||
<pre><code class="language-text">208
|
||||
151
|
||||
208
|
||||
180
|
||||
</code></pre>
|
||||
<p>But be sure to remember that valid Unicode scalar values may be made up of more
|
||||
than 1 byte.</p>
|
||||
<p>Getting grapheme clusters from strings, as with the Devanagari script, is
|
||||
complex, so this functionality is not provided by the standard library. Crates
|
||||
are available on <a href="https://crates.io/">crates.io</a><!-- ignore --> if this is the
|
||||
functionality you need.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="strings-are-not-so-simple"></a></p>
|
||||
<h3 id="handling-the-complexities-of-strings"><a class="header" href="#handling-the-complexities-of-strings">Handling the Complexities of Strings</a></h3>
|
||||
<p>To summarize, strings are complicated. Different programming languages make
|
||||
different choices about how to present this complexity to the programmer. Rust
|
||||
has chosen to make the correct handling of <code>String</code> data the default behavior
|
||||
for all Rust programs, which means programmers have to put more thought into
|
||||
handling UTF-8 data up front. This trade-off exposes more of the complexity of
|
||||
strings than is apparent in other programming languages, but it prevents you
|
||||
from having to handle errors involving non-ASCII characters later in your
|
||||
development life cycle.</p>
|
||||
<p>The good news is that the standard library offers a lot of functionality built
|
||||
off the <code>String</code> and <code>&str</code> types to help handle these complex situations
|
||||
correctly. Be sure to check out the documentation for useful methods like
|
||||
<code>contains</code> for searching in a string and <code>replace</code> for substituting parts of a
|
||||
string with another string.</p>
|
||||
<p>Let’s switch to something a bit less complex: hash maps!</p>
|
||||
</body>
|
||||
</html>
|
||||
262
ch08/ch08-03-hash-maps.html
Normal file
262
ch08/ch08-03-hash-maps.html
Normal file
@@ -0,0 +1,262 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Storing Keys with Associated Values in Hash Maps</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="storing-keys-with-associated-values-in-hash-maps"><a class="header" href="#storing-keys-with-associated-values-in-hash-maps">Storing Keys with Associated Values in Hash Maps</a></h2>
|
||||
<p>The last of our common collections is the hash map. The type <code>HashMap<K, V></code>
|
||||
stores a mapping of keys of type <code>K</code> to values of type <code>V</code> using a <em>hashing
|
||||
function</em>, which determines how it places these keys and values into memory.
|
||||
Many programming languages support this kind of data structure, but they often
|
||||
use a different name, such as <em>hash</em>, <em>map</em>, <em>object</em>, <em>hash table</em>,
|
||||
<em>dictionary</em>, or <em>associative array</em>, just to name a few.</p>
|
||||
<p>Hash maps are useful when you want to look up data not by using an index, as
|
||||
you can with vectors, but by using a key that can be of any type. For example,
|
||||
in a game, you could keep track of each team’s score in a hash map in which
|
||||
each key is a team’s name and the values are each team’s score. Given a team
|
||||
name, you can retrieve its score.</p>
|
||||
<p>We’ll go over the basic API of hash maps in this section, but many more goodies
|
||||
are hiding in the functions defined on <code>HashMap<K, V></code> by the standard library.
|
||||
As always, check the standard library documentation for more information.</p>
|
||||
<h3 id="creating-a-new-hash-map"><a class="header" href="#creating-a-new-hash-map">Creating a New Hash Map</a></h3>
|
||||
<p>One way to create an empty hash map is to use <code>new</code> and to add elements with
|
||||
<code>insert</code>. In Listing 8-20, we’re keeping track of the scores of two teams whose
|
||||
names are <em>Blue</em> and <em>Yellow</em>. The Blue team starts with 10 points, and the
|
||||
Yellow team starts with 50.</p>
|
||||
<figure class="listing" id="listing-8-20">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Yellow"), 50);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-20">Listing 8-20</a>: Creating a new hash map and inserting some keys and values</figcaption>
|
||||
</figure>
|
||||
<p>Note that we need to first <code>use</code> the <code>HashMap</code> from the collections portion of
|
||||
the standard library. Of our three common collections, this one is the least
|
||||
often used, so it’s not included in the features brought into scope
|
||||
automatically in the prelude. Hash maps also have less support from the
|
||||
standard library; there’s no built-in macro to construct them, for example.</p>
|
||||
<p>Just like vectors, hash maps store their data on the heap. This <code>HashMap</code> has
|
||||
keys of type <code>String</code> and values of type <code>i32</code>. Like vectors, hash maps are
|
||||
homogeneous: All of the keys must have the same type, and all of the values
|
||||
must have the same type.</p>
|
||||
<h3 id="accessing-values-in-a-hash-map"><a class="header" href="#accessing-values-in-a-hash-map">Accessing Values in a Hash Map</a></h3>
|
||||
<p>We can get a value out of the hash map by providing its key to the <code>get</code>
|
||||
method, as shown in Listing 8-21.</p>
|
||||
<figure class="listing" id="listing-8-21">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Yellow"), 50);
|
||||
|
||||
let team_name = String::from("Blue");
|
||||
let score = scores.get(&team_name).copied().unwrap_or(0);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-21">Listing 8-21</a>: Accessing the score for the Blue team stored in the hash map</figcaption>
|
||||
</figure>
|
||||
<p>Here, <code>score</code> will have the value that’s associated with the Blue team, and the
|
||||
result will be <code>10</code>. The <code>get</code> method returns an <code>Option<&V></code>; if there’s no
|
||||
value for that key in the hash map, <code>get</code> will return <code>None</code>. This program
|
||||
handles the <code>Option</code> by calling <code>copied</code> to get an <code>Option<i32></code> rather than an
|
||||
<code>Option<&i32></code>, then <code>unwrap_or</code> to set <code>score</code> to zero if <code>scores</code> doesn’t
|
||||
have an entry for the key.</p>
|
||||
<p>We can iterate over each key-value pair in a hash map in a similar manner as we
|
||||
do with vectors, using a <code>for</code> loop:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Yellow"), 50);
|
||||
|
||||
for (key, value) in &scores {
|
||||
println!("{key}: {value}");
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>This code will print each pair in an arbitrary order:</p>
|
||||
<pre><code class="language-text">Yellow: 50
|
||||
Blue: 10
|
||||
</code></pre>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="hash-maps-and-ownership"></a></p>
|
||||
<h3 id="managing-ownership-in-hash-maps"><a class="header" href="#managing-ownership-in-hash-maps">Managing Ownership in Hash Maps</a></h3>
|
||||
<p>For types that implement the <code>Copy</code> trait, like <code>i32</code>, the values are copied
|
||||
into the hash map. For owned values like <code>String</code>, the values will be moved and
|
||||
the hash map will be the owner of those values, as demonstrated in Listing 8-22.</p>
|
||||
<figure class="listing" id="listing-8-22">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let field_name = String::from("Favorite color");
|
||||
let field_value = String::from("Blue");
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(field_name, field_value);
|
||||
// field_name and field_value are invalid at this point, try using them and
|
||||
// see what compiler error you get!
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-22">Listing 8-22</a>: Showing that keys and values are owned by the hash map once they’re inserted</figcaption>
|
||||
</figure>
|
||||
<p>We aren’t able to use the variables <code>field_name</code> and <code>field_value</code> after
|
||||
they’ve been moved into the hash map with the call to <code>insert</code>.</p>
|
||||
<p>If we insert references to values into the hash map, the values won’t be moved
|
||||
into the hash map. The values that the references point to must be valid for at
|
||||
least as long as the hash map is valid. We’ll talk more about these issues in
|
||||
<a href="../ch10/ch10-03-lifetime-syntax.html#validating-references-with-lifetimes">“Validating References with
|
||||
Lifetimes”</a><!-- ignore --> in Chapter 10.</p>
|
||||
<h3 id="updating-a-hash-map"><a class="header" href="#updating-a-hash-map">Updating a Hash Map</a></h3>
|
||||
<p>Although the number of key and value pairs is growable, each unique key can
|
||||
only have one value associated with it at a time (but not vice versa: For
|
||||
example, both the Blue team and the Yellow team could have the value <code>10</code>
|
||||
stored in the <code>scores</code> hash map).</p>
|
||||
<p>When you want to change the data in a hash map, you have to decide how to
|
||||
handle the case when a key already has a value assigned. You could replace the
|
||||
old value with the new value, completely disregarding the old value. You could
|
||||
keep the old value and ignore the new value, only adding the new value if the
|
||||
key <em>doesn’t</em> already have a value. Or you could combine the old value and the
|
||||
new value. Let’s look at how to do each of these!</p>
|
||||
<h4 id="overwriting-a-value"><a class="header" href="#overwriting-a-value">Overwriting a Value</a></h4>
|
||||
<p>If we insert a key and a value into a hash map and then insert that same key
|
||||
with a different value, the value associated with that key will be replaced.
|
||||
Even though the code in Listing 8-23 calls <code>insert</code> twice, the hash map will
|
||||
only contain one key-value pair because we’re inserting the value for the Blue
|
||||
team’s key both times.</p>
|
||||
<figure class="listing" id="listing-8-23">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let mut scores = HashMap::new();
|
||||
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
scores.insert(String::from("Blue"), 25);
|
||||
|
||||
println!("{scores:?}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-23">Listing 8-23</a>: Replacing a value stored with a particular key</figcaption>
|
||||
</figure>
|
||||
<p>This code will print <code>{"Blue": 25}</code>. The original value of <code>10</code> has been
|
||||
overwritten.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="only-inserting-a-value-if-the-key-has-no-value"></a></p>
|
||||
<h4 id="adding-a-key-and-value-only-if-a-key-isnt-present"><a class="header" href="#adding-a-key-and-value-only-if-a-key-isnt-present">Adding a Key and Value Only If a Key Isn’t Present</a></h4>
|
||||
<p>It’s common to check whether a particular key already exists in the hash map
|
||||
with a value and then to take the following actions: If the key does exist in
|
||||
the hash map, the existing value should remain the way it is; if the key
|
||||
doesn’t exist, insert it and a value for it.</p>
|
||||
<p>Hash maps have a special API for this called <code>entry</code> that takes the key you
|
||||
want to check as a parameter. The return value of the <code>entry</code> method is an enum
|
||||
called <code>Entry</code> that represents a value that might or might not exist. Let’s say
|
||||
we want to check whether the key for the Yellow team has a value associated
|
||||
with it. If it doesn’t, we want to insert the value <code>50</code>, and the same for the
|
||||
Blue team. Using the <code>entry</code> API, the code looks like Listing 8-24.</p>
|
||||
<figure class="listing" id="listing-8-24">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let mut scores = HashMap::new();
|
||||
scores.insert(String::from("Blue"), 10);
|
||||
|
||||
scores.entry(String::from("Yellow")).or_insert(50);
|
||||
scores.entry(String::from("Blue")).or_insert(50);
|
||||
|
||||
println!("{scores:?}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-24">Listing 8-24</a>: Using the <code>entry</code> method to only insert if the key does not already have a value</figcaption>
|
||||
</figure>
|
||||
<p>The <code>or_insert</code> method on <code>Entry</code> is defined to return a mutable reference to
|
||||
the value for the corresponding <code>Entry</code> key if that key exists, and if not, it
|
||||
inserts the parameter as the new value for this key and returns a mutable
|
||||
reference to the new value. This technique is much cleaner than writing the
|
||||
logic ourselves and, in addition, plays more nicely with the borrow checker.</p>
|
||||
<p>Running the code in Listing 8-24 will print <code>{"Yellow": 50, "Blue": 10}</code>. The
|
||||
first call to <code>entry</code> will insert the key for the Yellow team with the value
|
||||
<code>50</code> because the Yellow team doesn’t have a value already. The second call to
|
||||
<code>entry</code> will not change the hash map, because the Blue team already has the
|
||||
value <code>10</code>.</p>
|
||||
<h4 id="updating-a-value-based-on-the-old-value"><a class="header" href="#updating-a-value-based-on-the-old-value">Updating a Value Based on the Old Value</a></h4>
|
||||
<p>Another common use case for hash maps is to look up a key’s value and then
|
||||
update it based on the old value. For instance, Listing 8-25 shows code that
|
||||
counts how many times each word appears in some text. We use a hash map with
|
||||
the words as keys and increment the value to keep track of how many times we’ve
|
||||
seen that word. If it’s the first time we’ve seen a word, we’ll first insert
|
||||
the value <code>0</code>.</p>
|
||||
<figure class="listing" id="listing-8-25">
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> use std::collections::HashMap;
|
||||
|
||||
let text = "hello world wonderful world";
|
||||
|
||||
let mut map = HashMap::new();
|
||||
|
||||
for word in text.split_whitespace() {
|
||||
let count = map.entry(word).or_insert(0);
|
||||
*count += 1;
|
||||
}
|
||||
|
||||
println!("{map:?}");
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-8-25">Listing 8-25</a>: Counting occurrences of words using a hash map that stores words and counts</figcaption>
|
||||
</figure>
|
||||
<p>This code will print <code>{"world": 2, "hello": 1, "wonderful": 1}</code>. You might see
|
||||
the same key-value pairs printed in a different order: Recall from <a href="#accessing-values-in-a-hash-map">“Accessing
|
||||
Values in a Hash Map”</a><!-- ignore --> that iterating over a hash map
|
||||
happens in an arbitrary order.</p>
|
||||
<p>The <code>split_whitespace</code> method returns an iterator over subslices, separated by
|
||||
whitespace, of the value in <code>text</code>. The <code>or_insert</code> method returns a mutable
|
||||
reference (<code>&mut V</code>) to the value for the specified key. Here, we store that
|
||||
mutable reference in the <code>count</code> variable, so in order to assign to that value,
|
||||
we must first dereference <code>count</code> using the asterisk (<code>*</code>). The mutable
|
||||
reference goes out of scope at the end of the <code>for</code> loop, so all of these
|
||||
changes are safe and allowed by the borrowing rules.</p>
|
||||
<h3 id="hashing-functions"><a class="header" href="#hashing-functions">Hashing Functions</a></h3>
|
||||
<p>By default, <code>HashMap</code> uses a hashing function called <em>SipHash</em> that can provide
|
||||
resistance to denial-of-service (DoS) attacks involving hash
|
||||
tables<sup class="footnote-reference" id="fr-siphash-1"><a href="#footnote-siphash">1</a></sup><!-- ignore -->. This is not the fastest hashing algorithm
|
||||
available, but the trade-off for better security that comes with the drop in
|
||||
performance is worth it. If you profile your code and find that the default
|
||||
hash function is too slow for your purposes, you can switch to another function
|
||||
by specifying a different hasher. A <em>hasher</em> is a type that implements the
|
||||
<code>BuildHasher</code> trait. We’ll talk about traits and how to implement them in
|
||||
<a href="../ch10/ch10-02-traits.html">Chapter 10</a><!-- ignore -->. You don’t necessarily have to implement
|
||||
your own hasher from scratch; <a href="https://crates.io/">crates.io</a><!-- ignore -->
|
||||
has libraries shared by other Rust users that provide hashers implementing many
|
||||
common hashing algorithms.</p>
|
||||
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
|
||||
<p>Vectors, strings, and hash maps will provide a large amount of functionality
|
||||
necessary in programs when you need to store, access, and modify data. Here are
|
||||
some exercises you should now be equipped to solve:</p>
|
||||
<ol>
|
||||
<li>Given a list of integers, use a vector and return the median (when sorted,
|
||||
the value in the middle position) and mode (the value that occurs most
|
||||
often; a hash map will be helpful here) of the list.</li>
|
||||
<li>Convert strings to Pig Latin. The first consonant of each word is moved to
|
||||
the end of the word and <em>ay</em> is added, so <em>first</em> becomes <em>irst-fay</em>. Words
|
||||
that start with a vowel have <em>hay</em> added to the end instead (<em>apple</em> becomes
|
||||
<em>apple-hay</em>). Keep in mind the details about UTF-8 encoding!</li>
|
||||
<li>Using a hash map and vectors, create a text interface to allow a user to add
|
||||
employee names to a department in a company; for example, “Add Sally to
|
||||
Engineering” or “Add Amir to Sales.” Then, let the user retrieve a list of
|
||||
all people in a department or all people in the company by department, sorted
|
||||
alphabetically.</li>
|
||||
</ol>
|
||||
<p>The standard library API documentation describes methods that vectors, strings,
|
||||
and hash maps have that will be helpful for these exercises!</p>
|
||||
<p>We’re getting into more complex programs in which operations can fail, so it’s
|
||||
a perfect time to discuss error handling. We’ll do that next!</p>
|
||||
<hr>
|
||||
<ol class="footnote-definition">
|
||||
<li id="footnote-siphash">
|
||||
<p><a href="https://en.wikipedia.org/wiki/SipHash">https://en.wikipedia.org/wiki/SipHash</a> <a href="#fr-siphash-1">↩</a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user