Files
docs-rust/ch10/ch10-00-generics.html
2026-06-22 21:27:36 +05:30

161 lines
8.0 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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&lt;T&gt;</code>, in Chapter 8 with <code>Vec&lt;T&gt;</code> and
<code>HashMap&lt;K, V&gt;</code>, and in Chapter 9 with <code>Result&lt;T, E&gt;</code>. In this chapter, youll
explore how to define your own types, functions, and methods with generics!</p>
<p>First, well review how to extract a function to reduce code duplication. Well
then use the same technique to make a generic function from two functions that
differ only in the types of their parameters. Well also explain how to use
generic types in struct and enum definitions.</p>
<p>Then, youll 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, well 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,
lets first look at how to remove duplication in a way that doesnt involve
generic types by extracting a function that replaces specific values with a
placeholder that represents multiple values. Then, well apply the same
technique to extract a generic function! By looking at how to recognize
duplicated code you can extract into a function, youll start to recognize
duplicated code that can use generics.</p>
<p>Well 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 = &amp;number_list[0];
for number in &amp;number_list {
if number &gt; 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 doesnt 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>Weve 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 = &amp;number_list[0];
for number in &amp;number_list {
if number &gt; largest {
largest = number;
}
}
println!("The largest number is {largest}");
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let mut largest = &amp;number_list[0];
for number in &amp;number_list {
if number &gt; 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, well 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: &amp;[i32]) -&gt; &amp;i32 {
let mut largest = &amp;list[0];
for item in list {
if item &gt; largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&amp;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(&amp;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, well 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? Lets find out!</p>
</body>
</html>