161 lines
8.0 KiB
HTML
161 lines
8.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Generic Types, Traits, and Lifetimes</title>
|
||
</head>
|
||
<body>
|
||
<h1 id="generic-types-traits-and-lifetimes"><a class="header" href="#generic-types-traits-and-lifetimes">Generic Types, Traits, and Lifetimes</a></h1>
|
||
<p>Every programming language has tools for effectively handling the duplication
|
||
of concepts. In Rust, one such tool is <em>generics</em>: abstract stand-ins for
|
||
concrete types or other properties. We can express the behavior of generics or
|
||
how they relate to other generics without knowing what will be in their place
|
||
when compiling and running the code.</p>
|
||
<p>Functions can take parameters of some generic type, instead of a concrete type
|
||
like <code>i32</code> or <code>String</code>, in the same way they take parameters with unknown
|
||
values to run the same code on multiple concrete values. In fact, we already
|
||
used generics in Chapter 6 with <code>Option<T></code>, in Chapter 8 with <code>Vec<T></code> and
|
||
<code>HashMap<K, V></code>, and in Chapter 9 with <code>Result<T, E></code>. In this chapter, you’ll
|
||
explore how to define your own types, functions, and methods with generics!</p>
|
||
<p>First, we’ll review how to extract a function to reduce code duplication. We’ll
|
||
then use the same technique to make a generic function from two functions that
|
||
differ only in the types of their parameters. We’ll also explain how to use
|
||
generic types in struct and enum definitions.</p>
|
||
<p>Then, you’ll learn how to use traits to define behavior in a generic way. You
|
||
can combine traits with generic types to constrain a generic type to accept
|
||
only those types that have a particular behavior, as opposed to just any type.</p>
|
||
<p>Finally, we’ll discuss <em>lifetimes</em>: a variety of generics that give the
|
||
compiler information about how references relate to each other. Lifetimes allow
|
||
us to give the compiler enough information about borrowed values so that it can
|
||
ensure that references will be valid in more situations than it could without
|
||
our help.</p>
|
||
<h2 id="removing-duplication-by-extracting-a-function"><a class="header" href="#removing-duplication-by-extracting-a-function">Removing Duplication by Extracting a Function</a></h2>
|
||
<p>Generics allow us to replace specific types with a placeholder that represents
|
||
multiple types to remove code duplication. Before diving into generics syntax,
|
||
let’s first look at how to remove duplication in a way that doesn’t involve
|
||
generic types by extracting a function that replaces specific values with a
|
||
placeholder that represents multiple values. Then, we’ll apply the same
|
||
technique to extract a generic function! By looking at how to recognize
|
||
duplicated code you can extract into a function, you’ll start to recognize
|
||
duplicated code that can use generics.</p>
|
||
<p>We’ll begin with the short program in Listing 10-1 that finds the largest
|
||
number in a list.</p>
|
||
<figure class="listing" id="listing-10-1">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let number_list = vec![34, 50, 25, 100, 65];
|
||
|
||
let mut largest = &number_list[0];
|
||
|
||
for number in &number_list {
|
||
if number > largest {
|
||
largest = number;
|
||
}
|
||
}
|
||
|
||
println!("The largest number is {largest}");
|
||
<span class="boring"> assert_eq!(*largest, 100);
|
||
</span>}</code></pre>
|
||
<figcaption><a href="#listing-10-1">Listing 10-1</a>: Finding the largest number in a list of numbers</figcaption>
|
||
</figure>
|
||
<p>We store a list of integers in the variable <code>number_list</code> and place a reference
|
||
to the first number in the list in a variable named <code>largest</code>. We then iterate
|
||
through all the numbers in the list, and if the current number is greater than
|
||
the number stored in <code>largest</code>, we replace the reference in that variable.
|
||
However, if the current number is less than or equal to the largest number seen
|
||
so far, the variable doesn’t change, and the code moves on to the next number
|
||
in the list. After considering all the numbers in the list, <code>largest</code> should
|
||
refer to the largest number, which in this case is 100.</p>
|
||
<p>We’ve now been tasked with finding the largest number in two different lists of
|
||
numbers. To do so, we can choose to duplicate the code in Listing 10-1 and use
|
||
the same logic at two different places in the program, as shown in Listing 10-2.</p>
|
||
<figure class="listing" id="listing-10-2">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn main() {
|
||
let number_list = vec![34, 50, 25, 100, 65];
|
||
|
||
let mut largest = &number_list[0];
|
||
|
||
for number in &number_list {
|
||
if number > largest {
|
||
largest = number;
|
||
}
|
||
}
|
||
|
||
println!("The largest number is {largest}");
|
||
|
||
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
|
||
|
||
let mut largest = &number_list[0];
|
||
|
||
for number in &number_list {
|
||
if number > largest {
|
||
largest = number;
|
||
}
|
||
}
|
||
|
||
println!("The largest number is {largest}");
|
||
}</code></pre>
|
||
<figcaption><a href="#listing-10-2">Listing 10-2</a>: Code to find the largest number in <em>two</em> lists of numbers</figcaption>
|
||
</figure>
|
||
<p>Although this code works, duplicating code is tedious and error-prone. We also
|
||
have to remember to update the code in multiple places when we want to change
|
||
it.</p>
|
||
<p>To eliminate this duplication, we’ll create an abstraction by defining a
|
||
function that operates on any list of integers passed in as a parameter. This
|
||
solution makes our code clearer and lets us express the concept of finding the
|
||
largest number in a list abstractly.</p>
|
||
<p>In Listing 10-3, we extract the code that finds the largest number into a
|
||
function named <code>largest</code>. Then, we call the function to find the largest number
|
||
in the two lists from Listing 10-2. We could also use the function on any other
|
||
list of <code>i32</code> values we might have in the future.</p>
|
||
<figure class="listing" id="listing-10-3">
|
||
<span class="file-name">Filename: src/main.rs</span>
|
||
<pre class="playground"><code class="language-rust edition2024">fn largest(list: &[i32]) -> &i32 {
|
||
let mut largest = &list[0];
|
||
|
||
for item in list {
|
||
if item > largest {
|
||
largest = item;
|
||
}
|
||
}
|
||
|
||
largest
|
||
}
|
||
|
||
fn main() {
|
||
let number_list = vec![34, 50, 25, 100, 65];
|
||
|
||
let result = largest(&number_list);
|
||
println!("The largest number is {result}");
|
||
<span class="boring"> assert_eq!(*result, 100);
|
||
</span>
|
||
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
|
||
|
||
let result = largest(&number_list);
|
||
println!("The largest number is {result}");
|
||
<span class="boring"> assert_eq!(*result, 6000);
|
||
</span>}</code></pre>
|
||
<figcaption><a href="#listing-10-3">Listing 10-3</a>: Abstracted code to find the largest number in two lists</figcaption>
|
||
</figure>
|
||
<p>The <code>largest</code> function has a parameter called <code>list</code>, which represents any
|
||
concrete slice of <code>i32</code> values we might pass into the function. As a result,
|
||
when we call the function, the code runs on the specific values that we pass
|
||
in.</p>
|
||
<p>In summary, here are the steps we took to change the code from Listing 10-2 to
|
||
Listing 10-3:</p>
|
||
<ol>
|
||
<li>Identify duplicate code.</li>
|
||
<li>Extract the duplicate code into the body of the function, and specify the
|
||
inputs and return values of that code in the function signature.</li>
|
||
<li>Update the two instances of duplicated code to call the function instead.</li>
|
||
</ol>
|
||
<p>Next, we’ll use these same steps with generics to reduce code duplication. In
|
||
the same way that the function body can operate on an abstract <code>list</code> instead
|
||
of specific values, generics allow code to operate on abstract types.</p>
|
||
<p>For example, say we had two functions: one that finds the largest item in a
|
||
slice of <code>i32</code> values and one that finds the largest item in a slice of <code>char</code>
|
||
values. How would we eliminate that duplication? Let’s find out!</p>
|
||
</body>
|
||
</html>
|