Files
rust-by-practice/lifetime/advance.html
sunface 987c59ed75 deploy
2025-04-25 12:48:03 +08:00

482 lines
20 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" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>advanced - Rust By Practice</title>
<!-- Custom HTML head -->
<meta name="description" content="Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../theme/style1.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust By Practice</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice/edit/master/en/src/lifetime/advance.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="advance-lifetime"><a class="header" href="#advance-lifetime">Advance lifetime</a></h1>
<h2 id="trait-bounds"><a class="header" href="#trait-bounds">Trait Bounds</a></h2>
<p>Just like generic types can be bounded, lifetimes can also be bounded as below:</p>
<ul>
<li><code>T: 'a</code>all references in <code>T</code> must outlive the lifetime <code>'a</code></li>
<li><code>T: Trait + 'a</code>: <code>T</code> must implement trait <code>Trait</code> and all references in <code>T</code> must outlive <code>'a</code></li>
</ul>
<p><strong>Example</strong></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fmt::Debug; // Trait to bound with.
#[derive(Debug)]
struct Ref&lt;'a, T: 'a&gt;(&amp;'a T);
// `Ref` contains a reference to a generic type `T` that has
// an unknown lifetime `'a`. `T` is bounded such that any
// *references* in `T` must outlive `'a`. Additionally, the lifetime
// of `Ref` may not exceed `'a`.
// A generic function which prints using the `Debug` trait.
fn print&lt;T&gt;(t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}
// Here a reference to `T` is taken where `T` implements
// `Debug` and all *references* in `T` outlive `'a`. In
// addition, `'a` must outlive the function.
fn print_ref&lt;'a, T&gt;(t: &amp;'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}
fn main() {
let x = 7;
let ref_x = Ref(&amp;x);
print_ref(&amp;ref_x);
print(ref_x);
}</code></pre></pre>
<ol>
<li>🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Annotate struct with lifetime:
1. `r` and `s` must have different lifetimes
2. lifetime of `s` is bigger than that of 'r'
*/
struct DoubleRef&lt;T&gt; {
r: &amp;T,
s: &amp;T
}
fn main() {
println!("Success!")
}</code></pre></pre>
<ol start="2">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Adding trait bounds to make it work */
struct ImportantExcerpt&lt;'a&gt; {
part: &amp;'a str,
}
impl&lt;'a, 'b&gt; ImportantExcerpt&lt;'a&gt; {
fn announce_and_return_part(&amp;'a self, announcement: &amp;'b str) -&gt; &amp;'b str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
println!("Success!")
}</code></pre></pre>
<ol start="3">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Adding trait bounds to make it work */
fn f&lt;'a, 'b&gt;(x: &amp;'a i32, mut y: &amp;'b i32) {
y = x;
let r: &amp;'b &amp;'a i32 = &amp;&amp;0;
}
fn main() {
println!("Success!")
}</code></pre></pre>
<h2 id="hrtbhigher-ranked-trait-bounds"><a class="header" href="#hrtbhigher-ranked-trait-bounds">HRTB(Higher-ranked trait bounds)</a></h2>
<p>Type bounds may be higher ranked over lifetimes. These bounds specify a bound is true for all lifetimes. For example, a bound such as <code>for&lt;'a&gt; &amp;'a T: PartialEq&lt;i32&gt;</code> would require an implementation like:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'a&gt; PartialEq&lt;i32&gt; for &amp;'a T {
// ...
}
<span class="boring">}</span></code></pre></pre>
<p>and could then be used to compare a <code>&amp;'a T</code> with any lifetime to an <code>i32</code>.</p>
<p>Only a higher-ranked bound can be used here, because the lifetime of the reference is shorter than any possible lifetime parameter on the function.</p>
<ol start="4">
<li>🌟🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Adding HRTB to make it work!*/
fn call_on_ref_zero&lt;'a, F&gt;(f: F) where F: Fn(&amp;'a i32) {
let zero = 0;
f(&amp;zero);
}
fn main() {
println!("Success!");
}</code></pre></pre>
<h2 id="nll-non-lexical-lifetime"><a class="header" href="#nll-non-lexical-lifetime">NLL (Non-Lexical Lifetime)</a></h2>
<p>Before explaining NLL, let's see some code first:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let mut s = String::from("hello");
let r1 = &amp;s;
let r2 = &amp;s;
println!("{} and {}", r1, r2);
let r3 = &amp;mut s;
println!("{}", r3);
}</code></pre></pre>
<p>Based on our current knowledge, this code will cause en error due to violating the borrowing rules in Rust.</p>
<p>But if you <code>cargo run</code> it, then everything will be ok, so what's going on here?</p>
<p>The ability of the compiler to tell that a reference is no longer used at a point before the end of the scope, is called <strong>Non-Lexical Lifetimes</strong> (<strong>NLL</strong> for short).</p>
<p>With this ability the compiler knows when is the last time that a reference is used and optimizing the borrowing rules based on this knowledge.</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let mut u = 0i32;
let mut v = 1i32;
let mut w = 2i32;
// lifetime of `a` = α β γ
let mut a = &amp;mut u; // --+ α. lifetime of `&amp;mut u` --+ lexical "lifetime" of `&amp;mut u`,`&amp;mut u`, `&amp;mut w` and `a`
use(a); // | |
*a = 3; // &lt;-----------------+ |
... // |
a = &amp;mut v; // --+ β. lifetime of `&amp;mut v` |
use(a); // | |
*a = 4; // &lt;-----------------+ |
... // |
a = &amp;mut w; // --+ γ. lifetime of `&amp;mut w` |
use(a); // | |
*a = 5; // &lt;-----------------+ &lt;--------------------------+
<span class="boring">}</span></code></pre></pre>
<h2 id="reborrow"><a class="header" href="#reborrow">Reborrow</a></h2>
<p>After learning NLL, we can easily understand reborrow now.</p>
<p><strong>Example</strong></p>
<pre><pre class="playground"><code class="language-rust edition2021">#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Point {
fn move_to(&amp;mut self, x: i32, y: i32) {
self.x = x;
self.y = y;
}
}
fn main() {
let mut p = Point { x: 0, y: 0 };
let r = &amp;mut p;
// Here comes the reborrow
let rr: &amp;Point = &amp;*r;
println!("{:?}", rr); // Reborrow ends here, NLL introduced
// Reborrow is over, we can continue using `r` now
r.move_to(10, 10);
println!("{:?}", r);
}</code></pre></pre>
<ol start="5">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work by reordering some code */
fn main() {
let mut data = 10;
let ref1 = &amp;mut data;
let ref2 = &amp;mut *ref1;
*ref1 += 1;
*ref2 += 2;
println!("{}", data);
}</code></pre></pre>
<h2 id="unbound-lifetime"><a class="header" href="#unbound-lifetime">Unbound lifetime</a></h2>
<p>See more info in <a href="https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html">Nomicon - Unbounded Lifetimes</a>.</p>
<h2 id="more-elision-rules"><a class="header" href="#more-elision-rules">More elision rules</a></h2>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;'a&gt; Reader for BufReader&lt;'a&gt; {
// 'a is not used in the following methods
}
// can be written as :
impl Reader for BufReader&lt;'_&gt; {
}
<span class="boring">}</span></code></pre></pre>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// Rust 2015
struct Ref&lt;'a, T: 'a&gt; {
field: &amp;'a T
}
// Rust 2018
struct Ref&lt;'a, T&gt; {
field: &amp;'a T
}
<span class="boring">}</span></code></pre></pre>
<h2 id="a-difficult-exercise"><a class="header" href="#a-difficult-exercise">A difficult exercise</a></h2>
<ol start="6">
<li>🌟🌟🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">/* Make it work */
struct Interface&lt;'a&gt; {
manager: &amp;'a mut Manager&lt;'a&gt;
}
impl&lt;'a&gt; Interface&lt;'a&gt; {
pub fn noop(self) {
println!("interface consumed");
}
}
struct Manager&lt;'a&gt; {
text: &amp;'a str
}
struct List&lt;'a&gt; {
manager: Manager&lt;'a&gt;,
}
impl&lt;'a&gt; List&lt;'a&gt; {
pub fn get_interface(&amp;'a mut self) -&gt; Interface {
Interface {
manager: &amp;mut self.manager
}
}
}
fn main() {
let mut list = List {
manager: Manager {
text: "hello"
}
};
list.get_interface().noop();
println!("Interface should be dropped here and the borrow released");
use_list(&amp;list);
}
fn use_list(list: &amp;List) {
println!("{}", list.manager.text);
}</code></pre></pre>
<blockquote>
<p>You can find the solutions <a href="https://github.com/sunface/rust-by-practice/blob/master/solutions/lifetime/advance.md">here</a>(under the solutions path), but only use it when you need it :)</p>
</blockquote>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../lifetime/static.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../functional-programing/intro.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../lifetime/static.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../functional-programing/intro.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../ace.js"></script>
<script src="../editor.js"></script>
<script src="../mode-rust.js"></script>
<script src="../theme-dawn.js"></script>
<script src="../theme-tomorrow_night.js"></script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../assets/custom3.js"></script>
<script src="../assets/lang1.js"></script>
</div>
</body>
</html>