You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

381 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE HTML>
<html lang="zh-CN" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>生命周期过大-02 - Rust语言圣经(Rust Course)</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<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/style.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语言圣经(Rust Course)</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-course" 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-course/edit/main/src/compiler/fight-with-compiler/lifetime/too-long2.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="生命周期过大-02"><a class="header" href="#生命周期过大-02">生命周期过大-02</a></h1>
<p>继上篇文章后,我们再来看一段<strong>可能</strong>涉及生命周期过大导致的无法编译问题:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn bar(writer: &amp;mut Writer) {
baz(writer.indent());
writer.write("world");
}
fn baz(writer: &amp;mut Writer) {
writer.write("hello");
}
pub struct Writer&lt;'a&gt; {
target: &amp;'a mut String,
indent: usize,
}
impl&lt;'a&gt; Writer&lt;'a&gt; {
fn indent(&amp;'a mut self) -&gt; &amp;'a mut Self {
&amp;mut Self {
target: self.target,
indent: self.indent + 1,
}
}
fn write(&amp;mut self, s: &amp;str) {
for _ in 0..self.indent {
self.target.push(' ');
}
self.target.push_str(s);
self.target.push('\n');
}
}
fn main() {}</code></pre></pre>
<p>报错如下:</p>
<pre><code class="language-console">error[E0623]: lifetime mismatch
--&gt; src/main.rs:2:16
|
1 | fn bar(writer: &amp;mut Writer) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | baz(writer.indent());
| ^^^^^^ ...but data from `writer` flows into `writer` here
</code></pre>
<p>WTF这什么报错之前都没有见过而且很难理解什么叫<code>writer</code>滑入了另一个<code>writer</code></p>
<p>别急,我们先来仔细看下代码,注意这一段:</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; Writer&lt;'a&gt; {
fn indent(&amp;'a mut self) -&gt; &amp;'a mut Self {
&amp;mut Self {
target: self.target,
indent: self.indent + 1,
}
}
<span class="boring">}</span></code></pre></pre>
<p>这里的生命周期定义说明<code>indent</code>方法使用的。。。等等!你的代码错了,你怎么能在一个函数中返回一个新创建实例的引用?!!最重要的是,编译器不提示这个错误,竟然提示一个莫名其妙看不懂的东东。</p>
<p>行,那我们先解决这个问题,将该方法修改为:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn indent(&amp;'a mut self) -&gt; Writer&lt;'a&gt; {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
<span class="boring">}</span></code></pre></pre>
<p>怀着惴惴这心,再一次运行程序,果不其然,编译器又朝我们扔了一坨错误:</p>
<pre><code class="language-console">error[E0308]: mismatched types
--&gt; src/main.rs:2:9
|
2 | baz(writer.indent());
| ^^^^^^^^^^^^^^^
| |
| expected `&amp;mut Writer&lt;'_&gt;`, found struct `Writer`
| help: consider mutably borrowing here: `&amp;mut writer.indent()`
</code></pre>
<p>哦,这次错误很明显,因为<code>baz</code>需要<code>&amp;mut Writer</code>,但是咱们<code>writer.indent</code>返回了一个<code>Writer</code>,因此修改下即可:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn bar(writer: &amp;mut Writer) {
baz(&amp;mut writer.indent());
writer.write("world");
}
<span class="boring">}</span></code></pre></pre>
<p>这次总该成功了吧?再次心慌慌的运行编译器,哐:</p>
<pre><code class="language-console">error[E0623]: lifetime mismatch
--&gt; src/main.rs:2:21
|
1 | fn bar(writer: &amp;mut Writer) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | baz(&amp;mut writer.indent());
| ^^^^^^ ...but data from `writer` flows into `writer` here
</code></pre>
<p>可恶,还是这个看不懂的错误,仔细检查了下代码,这次真的没有其他错误了,只能硬着头皮上。</p>
<p>大概的意思可以分析,生命周期范围不匹配,说明一个大一个小,然后一个<code>writer</code>中流入到另一个<code>writer</code>说明,两个<code>writer</code>的生命周期定义错了,既然这里提到了<code>indent</code>方法调用,那么我们再去仔细看一眼:</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; Writer&lt;'a&gt; {
fn indent(&amp;'a mut self) -&gt; Writer&lt;'a&gt; {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
...
}
<span class="boring">}</span></code></pre></pre>
<p>好像有点问题,<code>indent</code>返回的<code>Writer</code>的生命周期和外面调用者的<code>Writer</code>的生命周期一模一样,这很不合理,一眼就能看出前者远小于后者。</p>
<p>这里稍微展开以下,为何<code>indent</code>方法返回值的生命周期不能与参数中的<code>self</code>相同。<strong>首先,我们假设它们可以相同,也就是上面的代码可以编译通过</strong>,由于此时在返回值中借用了<code>self</code>的可变引用,意味着<strong>如果你在返回值被使用后,还继续使用<code>self</code> 会导致重复借用的错误,因为返回值的生命周期将持续到 <code>self</code> 结束</strong></p>
<p>既然不能相同,那我们尝试着修改下<code>indent</code>:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span> fn indent&lt;'b&gt;(&amp;'b mut self) -&gt; Writer&lt;'b&gt; {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
<span class="boring">}</span></code></pre></pre>
<p>Bang! 编译成功,不过稍等,回想下生命周期消除的规则,我们还可以实现的更优雅:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn bar(writer: &amp;mut Writer) {
baz(&amp;mut writer.indent());
writer.write("world");
}
fn baz(writer: &amp;mut Writer) {
writer.write("hello");
}
pub struct Writer&lt;'a&gt; {
target: &amp;'a mut String,
indent: usize,
}
impl&lt;'a&gt; Writer&lt;'a&gt; {
fn indent(&amp;mut self) -&gt; Writer {
Writer {
target: self.target,
indent: self.indent + 1,
}
}
fn write(&amp;mut self, s: &amp;str) {
for _ in 0..self.indent {
self.target.push(' ');
}
self.target.push_str(s);
self.target.push('\n');
}
}
fn main() {}</code></pre></pre>
<p>至此,问题彻底解决,太好了,我感觉我又变强了。可是默默看了眼自己的头发,只能以<code>哎~</code>一声叹息结束本章内容。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../../compiler/fight-with-compiler/lifetime/too-long1.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="../../../compiler/fight-with-compiler/lifetime/loop.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="../../../compiler/fight-with-compiler/lifetime/too-long1.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="../../../compiler/fight-with-compiler/lifetime/loop.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/custom2.js"></script>
<script src="../../../assets/bigPicture.js"></script>
</div>
</body>
</html>