Files
docs-rust/ch04/ch04-02-references-and-borrowing.html
2026-06-22 21:27:36 +05:30

353 lines
17 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>References and Borrowing</title>
</head>
<body>
<h2 id="references-and-borrowing"><a class="header" href="#references-and-borrowing">References and Borrowing</a></h2>
<p>The issue with the tuple code in Listing 4-5 is that we have to return the
<code>String</code> to the calling function so that we can still use the <code>String</code> after
the call to <code>calculate_length</code>, because the <code>String</code> was moved into
<code>calculate_length</code>. Instead, we can provide a reference to the <code>String</code> value.
A reference is like a pointer in that its an address we can follow to access
the data stored at that address; that data is owned by some other variable.
Unlike a pointer, a reference is guaranteed to point to a valid value of a
particular type for the life of that reference.</p>
<p>Here is how you would define and use a <code>calculate_length</code> function that has a
reference to an object as a parameter instead of taking ownership of the value:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024">fn main() {
let s1 = String::from("hello");
let len = calculate_length(&amp;s1);
println!("The length of '{s1}' is {len}.");
}
fn calculate_length(s: &amp;String) -&gt; usize {
s.len()
}</code></pre>
</figure>
<p>First, notice that all the tuple code in the variable declaration and the
function return value is gone. Second, note that we pass <code>&amp;s1</code> into
<code>calculate_length</code> and, in its definition, we take <code>&amp;String</code> rather than
<code>String</code>. These ampersands represent references, and they allow you to refer to
some value without taking ownership of it. Figure 4-6 depicts this concept.</p>
<p><img alt="Three tables: the table for s contains only a pointer to the table
for s1. The table for s1 contains the stack data for s1 and points to the
string data on the heap." src="../img/trpl04-06.svg" class="center" /></p>
<p><span class="caption">Figure 4-6: A diagram of <code>&amp;String</code> <code>s</code> pointing at
<code>String</code> <code>s1</code></span></p>
<section class="note" aria-role="note">
<p>Note: The opposite of referencing by using <code>&amp;</code> is <em>dereferencing</em>, which is
accomplished with the dereference operator, <code>*</code>. Well see some uses of the
dereference operator in Chapter 8 and discuss details of dereferencing in
Chapter 15.</p>
</section>
<p>Lets take a closer look at the function call here:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let s1 = String::from("hello");
let len = calculate_length(&amp;s1);
<span class="boring">
</span><span class="boring"> println!("The length of '{s1}' is {len}.");
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn calculate_length(s: &amp;String) -&gt; usize {
</span><span class="boring"> s.len()
</span><span class="boring">}</span></code></pre>
<p>The <code>&amp;s1</code> syntax lets us create a reference that <em>refers</em> to the value of <code>s1</code>
but does not own it. Because the reference does not own it, the value it points
to will not be dropped when the reference stops being used.</p>
<p>Likewise, the signature of the function uses <code>&amp;</code> to indicate that the type of
the parameter <code>s</code> is a reference. Lets add some explanatory annotations:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span><span class="boring"> let s1 = String::from("hello");
</span><span class="boring">
</span><span class="boring"> let len = calculate_length(&amp;s1);
</span><span class="boring">
</span><span class="boring"> println!("The length of '{s1}' is {len}.");
</span><span class="boring">}
</span><span class="boring">
</span>fn calculate_length(s: &amp;String) -&gt; usize { // s is a reference to a String
s.len()
} // Here, s goes out of scope. But because s does not have ownership of what
// it refers to, the String is not dropped.</code></pre>
<p>The scope in which the variable <code>s</code> is valid is the same as any function
parameters scope, but the value pointed to by the reference is not dropped
when <code>s</code> stops being used, because <code>s</code> doesnt have ownership. When functions
have references as parameters instead of the actual values, we wont need to
return the values in order to give back ownership, because we never had
ownership.</p>
<p>We call the action of creating a reference <em>borrowing</em>. As in real life, if a
person owns something, you can borrow it from them. When youre done, you have
to give it back. You dont own it.</p>
<p>So, what happens if we try to modify something were borrowing? Try the code in
Listing 4-6. Spoiler alert: It doesnt work!</p>
<figure class="listing" id="listing-4-6">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile">fn main() {
let s = String::from("hello");
change(&amp;s);
}
fn change(some_string: &amp;String) {
some_string.push_str(", world");
}</code></pre>
<figcaption><a href="#listing-4-6">Listing 4-6</a>: Attempting to modify a borrowed value</figcaption>
</figure>
<p>Heres the error:</p>
<pre><code class="language-console">$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&amp;` reference
--&gt; src/main.rs:8:5
|
8 | some_string.push_str(", world");
| ^^^^^^^^^^^ `some_string` is a `&amp;` reference, so the data it refers to cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
7 | fn change(some_string: &amp;mut String) {
| +++
For more information about this error, try `rustc --explain E0596`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error
</code></pre>
<p>Just as variables are immutable by default, so are references. Were not
allowed to modify something we have a reference to.</p>
<h3 id="mutable-references"><a class="header" href="#mutable-references">Mutable References</a></h3>
<p>We can fix the code from Listing 4-6 to allow us to modify a borrowed value
with just a few small tweaks that use, instead, a <em>mutable reference</em>:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre class="playground"><code class="language-rust edition2024">fn main() {
let mut s = String::from("hello");
change(&amp;mut s);
}
fn change(some_string: &amp;mut String) {
some_string.push_str(", world");
}</code></pre>
</figure>
<p>First, we change <code>s</code> to be <code>mut</code>. Then, we create a mutable reference with
<code>&amp;mut s</code> where we call the <code>change</code> function and update the function signature
to accept a mutable reference with <code>some_string: &amp;mut String</code>. This makes it
very clear that the <code>change</code> function will mutate the value it borrows.</p>
<p>Mutable references have one big restriction: If you have a mutable reference to
a value, you can have no other references to that value. This code that
attempts to create two mutable references to <code>s</code> will fail:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
</span> let mut s = String::from("hello");
let r1 = &amp;mut s;
let r2 = &amp;mut s;
println!("{r1}, {r2}");
<span class="boring">}</span></code></pre>
</figure>
<p>Heres the error:</p>
<pre><code class="language-console">$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0499]: cannot borrow `s` as mutable more than once at a time
--&gt; src/main.rs:5:14
|
4 | let r1 = &amp;mut s;
| ------ first mutable borrow occurs here
5 | let r2 = &amp;mut s;
| ^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{r1}, {r2}");
| -- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error
</code></pre>
<p>This error says that this code is invalid because we cannot borrow <code>s</code> as
mutable more than once at a time. The first mutable borrow is in <code>r1</code> and must
last until its used in the <code>println!</code>, but between the creation of that
mutable reference and its usage, we tried to create another mutable reference
in <code>r2</code> that borrows the same data as <code>r1</code>.</p>
<p>The restriction preventing multiple mutable references to the same data at the
same time allows for mutation but in a very controlled fashion. Its something
that new Rustaceans struggle with because most languages let you mutate
whenever youd like. The benefit of having this restriction is that Rust can
prevent data races at compile time. A <em>data race</em> is similar to a race
condition and happens when these three behaviors occur:</p>
<ul>
<li>Two or more pointers access the same data at the same time.</li>
<li>At least one of the pointers is being used to write to the data.</li>
<li>Theres no mechanism being used to synchronize access to the data.</li>
</ul>
<p>Data races cause undefined behavior and can be difficult to diagnose and fix
when youre trying to track them down at runtime; Rust prevents this problem by
refusing to compile code with data races!</p>
<p>As always, we can use curly brackets to create a new scope, allowing for
multiple mutable references, just not <em>simultaneous</em> ones:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let mut s = String::from("hello");
{
let r1 = &amp;mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.
let r2 = &amp;mut s;
<span class="boring">}</span></code></pre>
<p>Rust enforces a similar rule for combining mutable and immutable references.
This code results in an error:</p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
</span> let mut s = String::from("hello");
let r1 = &amp;s; // no problem
let r2 = &amp;s; // no problem
let r3 = &amp;mut s; // BIG PROBLEM
println!("{r1}, {r2}, and {r3}");
<span class="boring">}</span></code></pre>
<p>Heres the error:</p>
<pre><code class="language-console">$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--&gt; src/main.rs:6:14
|
4 | let r1 = &amp;s; // no problem
| -- immutable borrow occurs here
5 | let r2 = &amp;s; // no problem
6 | let r3 = &amp;mut s; // BIG PROBLEM
| ^^^^^^ mutable borrow occurs here
7 |
8 | println!("{r1}, {r2}, and {r3}");
| -- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error
</code></pre>
<p>Whew! We <em>also</em> cannot have a mutable reference while we have an immutable one
to the same value.</p>
<p>Users of an immutable reference dont expect the value to suddenly change out
from under them! However, multiple immutable references are allowed because no
one who is just reading the data has the ability to affect anyone elses
reading of the data.</p>
<p>Note that a references scope starts from where it is introduced and continues
through the last time that reference is used. For instance, this code will
compile because the last usage of the immutable references is in the <code>println!</code>,
before the mutable reference is introduced:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let mut s = String::from("hello");
let r1 = &amp;s; // no problem
let r2 = &amp;s; // no problem
println!("{r1} and {r2}");
// Variables r1 and r2 will not be used after this point.
let r3 = &amp;mut s; // no problem
println!("{r3}");
<span class="boring">}</span></code></pre>
<p>The scopes of the immutable references <code>r1</code> and <code>r2</code> end after the <code>println!</code>
where they are last used, which is before the mutable reference <code>r3</code> is
created. These scopes dont overlap, so this code is allowed: The compiler can
tell that the reference is no longer being used at a point before the end of
the scope.</p>
<p>Even though borrowing errors may be frustrating at times, remember that its
the Rust compiler pointing out a potential bug early (at compile time rather
than at runtime) and showing you exactly where the problem is. Then, you dont
have to track down why your data isnt what you thought it was.</p>
<h3 id="dangling-references"><a class="header" href="#dangling-references">Dangling References</a></h3>
<p>In languages with pointers, its easy to erroneously create a <em>dangling
pointer</em>—a pointer that references a location in memory that may have been
given to someone else—by freeing some memory while preserving a pointer to that
memory. In Rust, by contrast, the compiler guarantees that references will
never be dangling references: If you have a reference to some data, the
compiler will ensure that the data will not go out of scope before the
reference to the data does.</p>
<p>Lets try to create a dangling reference to see how Rust prevents them with a
compile-time error:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile">fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -&gt; &amp;String {
let s = String::from("hello");
&amp;s
}</code></pre>
</figure>
<p>Heres the error:</p>
<pre><code class="language-console">$ cargo run
Compiling ownership v0.1.0 (file:///projects/ownership)
error[E0106]: missing lifetime specifier
--&gt; src/main.rs:5:16
|
5 | fn dangle() -&gt; &amp;String {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
|
5 | fn dangle() -&gt; &amp;'static String {
| +++++++
help: instead, you are more likely to want to return an owned value
|
5 - fn dangle() -&gt; &amp;String {
5 + fn dangle() -&gt; String {
|
For more information about this error, try `rustc --explain E0106`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error
</code></pre>
<p>This error message refers to a feature we havent covered yet: lifetimes. Well
discuss lifetimes in detail in Chapter 10. But, if you disregard the parts
about lifetimes, the message does contain the key to why this code is a problem:</p>
<pre><code class="language-text">this function's return type contains a borrowed value, but there is no value
for it to be borrowed from
</code></pre>
<p>Lets take a closer look at exactly whats happening at each stage of our
<code>dangle</code> code:</p>
<figure class="listing">
<span class="file-name">Filename: src/main.rs</span>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
</span><span class="boring"> let reference_to_nothing = dangle();
</span><span class="boring">}
</span><span class="boring">
</span>fn dangle() -&gt; &amp;String { // dangle returns a reference to a String
let s = String::from("hello"); // s is a new String
&amp;s // we return a reference to the String, s
} // Here, s goes out of scope and is dropped, so its memory goes away.
// Danger!</code></pre>
</figure>
<p>Because <code>s</code> is created inside <code>dangle</code>, when the code of <code>dangle</code> is finished,
<code>s</code> will be deallocated. But we tried to return a reference to it. That means
this reference would be pointing to an invalid <code>String</code>. Thats no good! Rust
wont let us do this.</p>
<p>The solution here is to return the <code>String</code> directly:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span><span class="boring"> let string = no_dangle();
</span><span class="boring">}
</span><span class="boring">
</span>fn no_dangle() -&gt; String {
let s = String::from("hello");
s
}</code></pre>
<p>This works without any problems. Ownership is moved out, and nothing is
deallocated.</p>
<h3 id="the-rules-of-references"><a class="header" href="#the-rules-of-references">The Rules of References</a></h3>
<p>Lets recap what weve discussed about references:</p>
<ul>
<li>At any given time, you can have <em>either</em> one mutable reference <em>or</em> any
number of immutable references.</li>
<li>References must always be valid.</li>
</ul>
<p>Next, well look at a different kind of reference: slices.</p>
</body>
</html>