fix outdated links re: #13

This commit is contained in:
Carl Fredrik Samson
2020-04-18 02:28:53 +02:00
parent a4c161c060
commit f4deaaf87b
20 changed files with 1647 additions and 1900 deletions

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -280,199 +277,199 @@ It's not in any way meant to showcase &quot;best practice&quot;. Just so we're o
the same page.</p>
</blockquote>
<p><em><strong>Press the expand icon in the top right corner to show the example code.</strong></em></p>
<pre><pre class="playpen"><code class="language-rust edition2018"><span class="boring">#![feature(asm, naked_functions)]
</span><span class="boring">use std::ptr;
</span><span class="boring">
</span><span class="boring">const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
</span><span class="boring">const MAX_THREADS: usize = 4;
</span><span class="boring">static mut RUNTIME: usize = 0;
</span><span class="boring">
</span><span class="boring">pub struct Runtime {
</span><span class="boring"> threads: Vec&lt;Thread&gt;,
</span><span class="boring"> current: usize,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[derive(PartialEq, Eq, Debug)]
</span><span class="boring">enum State {
</span><span class="boring"> Available,
</span><span class="boring"> Running,
</span><span class="boring"> Ready,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">struct Thread {
</span><span class="boring"> id: usize,
</span><span class="boring"> stack: Vec&lt;u8&gt;,
</span><span class="boring"> ctx: ThreadContext,
</span><span class="boring"> state: State,
</span><span class="boring"> task: Option&lt;Box&lt;dyn Fn()&gt;&gt;,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[derive(Debug, Default)]
</span><span class="boring">#[repr(C)]
</span><span class="boring">struct ThreadContext {
</span><span class="boring"> rsp: u64,
</span><span class="boring"> r15: u64,
</span><span class="boring"> r14: u64,
</span><span class="boring"> r13: u64,
</span><span class="boring"> r12: u64,
</span><span class="boring"> rbx: u64,
</span><span class="boring"> rbp: u64,
</span><span class="boring"> thread_ptr: u64,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Thread {
</span><span class="boring"> fn new(id: usize) -&gt; Self {
</span><span class="boring"> Thread {
</span><span class="boring"> id,
</span><span class="boring"> stack: vec![0_u8; DEFAULT_STACK_SIZE],
</span><span class="boring"> ctx: ThreadContext::default(),
</span><span class="boring"> state: State::Available,
</span><span class="boring"> task: None,
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Runtime {
</span><span class="boring"> pub fn new() -&gt; Self {
</span><span class="boring"> let base_thread = Thread {
</span><span class="boring"> id: 0,
</span><span class="boring"> stack: vec![0_u8; DEFAULT_STACK_SIZE],
</span><span class="boring"> ctx: ThreadContext::default(),
</span><span class="boring"> state: State::Running,
</span><span class="boring"> task: None,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let mut threads = vec![base_thread];
</span><span class="boring"> threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64;
</span><span class="boring"> let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
</span><span class="boring"> threads.append(&amp;mut available_threads);
</span><span class="boring">
</span><span class="boring"> Runtime {
</span><span class="boring"> threads,
</span><span class="boring"> current: 0,
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn init(&amp;self) {
</span><span class="boring"> unsafe {
</span><span class="boring"> let r_ptr: *const Runtime = self;
</span><span class="boring"> RUNTIME = r_ptr as usize;
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn run(&amp;mut self) -&gt; ! {
</span><span class="boring"> while self.t_yield() {}
</span><span class="boring"> std::process::exit(0);
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn t_return(&amp;mut self) {
</span><span class="boring"> if self.current != 0 {
</span><span class="boring"> self.threads[self.current].state = State::Available;
</span><span class="boring"> self.t_yield();
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn t_yield(&amp;mut self) -&gt; bool {
</span><span class="boring"> let mut pos = self.current;
</span><span class="boring"> while self.threads[pos].state != State::Ready {
</span><span class="boring"> pos += 1;
</span><span class="boring"> if pos == self.threads.len() {
</span><span class="boring"> pos = 0;
</span><span class="boring"> }
</span><span class="boring"> if pos == self.current {
</span><span class="boring"> return false;
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> if self.threads[self.current].state != State::Available {
</span><span class="boring"> self.threads[self.current].state = State::Ready;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> self.threads[pos].state = State::Running;
</span><span class="boring"> let old_pos = self.current;
</span><span class="boring"> self.current = pos;
</span><span class="boring">
</span><span class="boring"> unsafe {
</span><span class="boring"> switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
</span><span class="boring"> }
</span><span class="boring"> true
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){
</span><span class="boring"> unsafe {
</span><span class="boring"> let rt_ptr = RUNTIME as *mut Runtime;
</span><span class="boring"> let available = (*rt_ptr)
</span><span class="boring"> .threads
</span><span class="boring"> .iter_mut()
</span><span class="boring"> .find(|t| t.state == State::Available)
</span><span class="boring"> .expect(&quot;no available thread.&quot;);
</span><span class="boring">
</span><span class="boring"> let size = available.stack.len();
</span><span class="boring"> let s_ptr = available.stack.as_mut_ptr();
</span><span class="boring"> available.task = Some(Box::new(f));
</span><span class="boring"> available.ctx.thread_ptr = available as *const Thread as u64;
</span><span class="boring"> ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
</span><span class="boring"> ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
</span><span class="boring"> available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
</span><span class="boring"> available.state = State::Ready;
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn call(thread: u64) {
</span><span class="boring"> let thread = unsafe { &amp;*(thread as *const Thread) };
</span><span class="boring"> if let Some(f) = &amp;thread.task {
</span><span class="boring"> f();
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[naked]
</span><span class="boring">fn guard() {
</span><span class="boring"> unsafe {
</span><span class="boring"> let rt_ptr = RUNTIME as *mut Runtime;
</span><span class="boring"> let rt = &amp;mut *rt_ptr;
</span><span class="boring"> println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id);
</span><span class="boring"> rt.t_return();
</span><span class="boring"> };
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">pub fn yield_thread() {
</span><span class="boring"> unsafe {
</span><span class="boring"> let rt_ptr = RUNTIME as *mut Runtime;
</span><span class="boring"> (*rt_ptr).t_yield();
</span><span class="boring"> };
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[naked]
</span><span class="boring">#[inline(never)]
</span><span class="boring">unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
</span><span class="boring"> asm!(&quot;
</span><span class="boring"> mov %rsp, 0x00($0)
</span><span class="boring"> mov %r15, 0x08($0)
</span><span class="boring"> mov %r14, 0x10($0)
</span><span class="boring"> mov %r13, 0x18($0)
</span><span class="boring"> mov %r12, 0x20($0)
</span><span class="boring"> mov %rbx, 0x28($0)
</span><span class="boring"> mov %rbp, 0x30($0)
</span><span class="boring">
</span><span class="boring"> mov 0x00($1), %rsp
</span><span class="boring"> mov 0x08($1), %r15
</span><span class="boring"> mov 0x10($1), %r14
</span><span class="boring"> mov 0x18($1), %r13
</span><span class="boring"> mov 0x20($1), %r12
</span><span class="boring"> mov 0x28($1), %rbx
</span><span class="boring"> mov 0x30($1), %rbp
</span><span class="boring"> mov 0x38($1), %rdi
</span><span class="boring"> ret
</span><span class="boring"> &quot;
</span><span class="boring"> :
</span><span class="boring"> : &quot;r&quot;(old), &quot;r&quot;(new)
</span><span class="boring"> :
</span><span class="boring"> : &quot;alignstack&quot;
</span><span class="boring"> );
</span><span class="boring">}
</span><span class="boring">#[cfg(not(windows))]
</span>fn main() {
<pre><pre class="playpen"><code class="language-rust edition2018"># #![feature(asm, naked_functions)]
# use std::ptr;
#
# const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
# const MAX_THREADS: usize = 4;
# static mut RUNTIME: usize = 0;
#
# pub struct Runtime {
# threads: Vec&lt;Thread&gt;,
# current: usize,
# }
#
# #[derive(PartialEq, Eq, Debug)]
# enum State {
# Available,
# Running,
# Ready,
# }
#
# struct Thread {
# id: usize,
# stack: Vec&lt;u8&gt;,
# ctx: ThreadContext,
# state: State,
# task: Option&lt;Box&lt;dyn Fn()&gt;&gt;,
# }
#
# #[derive(Debug, Default)]
# #[repr(C)]
# struct ThreadContext {
# rsp: u64,
# r15: u64,
# r14: u64,
# r13: u64,
# r12: u64,
# rbx: u64,
# rbp: u64,
# thread_ptr: u64,
# }
#
# impl Thread {
# fn new(id: usize) -&gt; Self {
# Thread {
# id,
# stack: vec![0_u8; DEFAULT_STACK_SIZE],
# ctx: ThreadContext::default(),
# state: State::Available,
# task: None,
# }
# }
# }
#
# impl Runtime {
# pub fn new() -&gt; Self {
# let base_thread = Thread {
# id: 0,
# stack: vec![0_u8; DEFAULT_STACK_SIZE],
# ctx: ThreadContext::default(),
# state: State::Running,
# task: None,
# };
#
# let mut threads = vec![base_thread];
# threads[0].ctx.thread_ptr = &amp;threads[0] as *const Thread as u64;
# let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
# threads.append(&amp;mut available_threads);
#
# Runtime {
# threads,
# current: 0,
# }
# }
#
# pub fn init(&amp;self) {
# unsafe {
# let r_ptr: *const Runtime = self;
# RUNTIME = r_ptr as usize;
# }
# }
#
# pub fn run(&amp;mut self) -&gt; ! {
# while self.t_yield() {}
# std::process::exit(0);
# }
#
# fn t_return(&amp;mut self) {
# if self.current != 0 {
# self.threads[self.current].state = State::Available;
# self.t_yield();
# }
# }
#
# fn t_yield(&amp;mut self) -&gt; bool {
# let mut pos = self.current;
# while self.threads[pos].state != State::Ready {
# pos += 1;
# if pos == self.threads.len() {
# pos = 0;
# }
# if pos == self.current {
# return false;
# }
# }
#
# if self.threads[self.current].state != State::Available {
# self.threads[self.current].state = State::Ready;
# }
#
# self.threads[pos].state = State::Running;
# let old_pos = self.current;
# self.current = pos;
#
# unsafe {
# switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
# }
# true
# }
#
# pub fn spawn&lt;F: Fn() + 'static&gt;(f: F){
# unsafe {
# let rt_ptr = RUNTIME as *mut Runtime;
# let available = (*rt_ptr)
# .threads
# .iter_mut()
# .find(|t| t.state == State::Available)
# .expect(&quot;no available thread.&quot;);
#
# let size = available.stack.len();
# let s_ptr = available.stack.as_mut_ptr();
# available.task = Some(Box::new(f));
# available.ctx.thread_ptr = available as *const Thread as u64;
# ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
# ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
# available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
# available.state = State::Ready;
# }
# }
# }
#
# fn call(thread: u64) {
# let thread = unsafe { &amp;*(thread as *const Thread) };
# if let Some(f) = &amp;thread.task {
# f();
# }
# }
#
# #[naked]
# fn guard() {
# unsafe {
# let rt_ptr = RUNTIME as *mut Runtime;
# let rt = &amp;mut *rt_ptr;
# println!(&quot;THREAD {} FINISHED.&quot;, rt.threads[rt.current].id);
# rt.t_return();
# };
# }
#
# pub fn yield_thread() {
# unsafe {
# let rt_ptr = RUNTIME as *mut Runtime;
# (*rt_ptr).t_yield();
# };
# }
#
# #[naked]
# #[inline(never)]
# unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
# asm!(&quot;
# mov %rsp, 0x00($0)
# mov %r15, 0x08($0)
# mov %r14, 0x10($0)
# mov %r13, 0x18($0)
# mov %r12, 0x20($0)
# mov %rbx, 0x28($0)
# mov %rbp, 0x30($0)
#
# mov 0x00($1), %rsp
# mov 0x08($1), %r15
# mov 0x10($1), %r14
# mov 0x18($1), %r13
# mov 0x20($1), %r12
# mov 0x28($1), %rbx
# mov 0x30($1), %rbp
# mov 0x38($1), %rdi
# ret
# &quot;
# :
# : &quot;r&quot;(old), &quot;r&quot;(new)
# :
# : &quot;alignstack&quot;
# );
# }
# #[cfg(not(windows))]
fn main() {
let mut runtime = Runtime::new();
runtime.init();
Runtime::spawn(|| {
@@ -488,9 +485,9 @@ the same page.</p>
});
runtime.run();
}
<span class="boring">#[cfg(windows)]
</span><span class="boring">fn main() { }
</span></code></pre></pre>
# #[cfg(windows)]
# fn main() { }
</code></pre></pre>
<p>Still hanging in there? Good. Don't get frustrated if the code above is
difficult to understand. If I hadn't written it myself I would probably feel
the same. You can always go back and read the book which explains it later.</p>
@@ -662,7 +659,7 @@ need to be polled once before they do any work.</p>
</blockquote>
<br />
<div style="text-align: center; padding-top: 2em;">
<a href="/books-futures-explained/1_futures_in_rust.html" style="background: red; color: white; padding:2em 2em 2em 2em; font-size: 1.2em;"><strong>PANIC BUTTON (next chapter)</strong></a>
<a href="/books-futures-explained/1_futures_in_rust.html" style="display: inline-block; background: red; color: white; padding:2em 2em 2em 2em; font-size: 1.2em;"><strong>PANIC BUTTON (next chapter)</strong></a>
</div>
</main>
@@ -739,18 +736,6 @@ need to be polled once before they do any work.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html" class="active"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html" class="active"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -403,18 +400,6 @@ it needs to be, so go on and read these chapters if you feel a bit unsure.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html" class="active"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html" class="active"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -194,8 +191,8 @@ article written by Adam Schwalm called <a href="https://alschwalm.com/blog/stati
to take a step back and talk about some fundamentals. Let's start by taking a
look at the size of some different pointer types in Rust.</p>
<p>Run the following code <em>(You'll have to press &quot;play&quot; to see the output)</em>:</p>
<pre><pre class="playpen"><code class="language-rust"><span class="boring">use std::mem::size_of;
</span>trait SomeTrait { }
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
trait SomeTrait { }
fn main() {
println!(&quot;======== The size of different pointers in Rust: ========&quot;);
@@ -380,18 +377,6 @@ use purely global functions and state, or any other way you wish.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html" class="active"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html" class="active"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -363,19 +360,19 @@ trait for our generators which would let us do this:</p>
Just keep this in the back of your head as we move forward.</p>
<p>Now what does our rewritten state machine look like with this example?</p>
<pre><pre class="playpen"><code class="language-rust compile_fail">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span><span class="boring">enum GeneratorState&lt;Y, R&gt; {
</span><span class="boring"> Yielded(Y),
</span><span class="boring"> Complete(R),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">trait Generator {
</span><span class="boring"> type Yield;
</span><span class="boring"> type Return;
</span><span class="boring"> fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
</span><span class="boring">}
</span>
# #![allow(unused_variables)]
#fn main() {
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Complete(R),
# }
#
# trait Generator {
# type Yield;
# type Return;
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
# }
enum GeneratorA {
Enter,
Yield1 {
@@ -385,12 +382,12 @@ enum GeneratorA {
Exit,
}
<span class="boring">impl GeneratorA {
</span><span class="boring"> fn start() -&gt; Self {
</span><span class="boring"> GeneratorA::Enter
</span><span class="boring"> }
</span><span class="boring">}
</span>
# impl GeneratorA {
# fn start() -&gt; Self {
# GeneratorA::Enter
# }
# }
impl Generator for GeneratorA {
type Yield = usize;
type Return = ();
@@ -415,8 +412,7 @@ impl Generator for GeneratorA {
}
}
}
<span class="boring">}
</span></code></pre></pre>
#}</code></pre></pre>
<p>If you try to compile this you'll get an error (just try it yourself by pressing play).</p>
<p>What is the lifetime of <code>&amp;String</code>. It's not the same as the lifetime of <code>Self</code>. It's not <code>static</code>.
Turns out that it's not possible for us in Rusts syntax to describe this lifetime, which means, that
@@ -427,9 +423,9 @@ see we end up in a <em>self referential struct</em>. A struct which holds refere
into itself.</p>
<p>As you'll notice, this compiles just fine!</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>enum GeneratorState&lt;Y, R&gt; {
# #![allow(unused_variables)]
#fn main() {
enum GeneratorState&lt;Y, R&gt; {
Yielded(Y),
Complete(R),
}
@@ -483,8 +479,7 @@ impl Generator for GeneratorA {
}
}
}
<span class="boring">}
</span></code></pre></pre>
#}</code></pre></pre>
<p>Remember that our example is the generator we crated which looked like this:</p>
<pre><code class="language-rust noplaypen ignore">let mut gen = move || {
let to_borrow = String::from(&quot;Hello&quot;);
@@ -511,66 +506,66 @@ does what we'd expect. But there is still one huge problem with this:</p>
()
};
}
<span class="boring">enum GeneratorState&lt;Y, R&gt; {
</span><span class="boring"> Yielded(Y),
</span><span class="boring"> Complete(R),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">trait Generator {
</span><span class="boring"> type Yield;
</span><span class="boring"> type Return;
</span><span class="boring"> fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum GeneratorA {
</span><span class="boring"> Enter,
</span><span class="boring"> Yield1 {
</span><span class="boring"> to_borrow: String,
</span><span class="boring"> borrowed: *const String,
</span><span class="boring"> },
</span><span class="boring"> Exit,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl GeneratorA {
</span><span class="boring"> fn start() -&gt; Self {
</span><span class="boring"> GeneratorA::Enter
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">impl Generator for GeneratorA {
</span><span class="boring"> type Yield = usize;
</span><span class="boring"> type Return = ();
</span><span class="boring"> fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt; {
</span><span class="boring"> match self {
</span><span class="boring"> GeneratorA::Enter =&gt; {
</span><span class="boring"> let to_borrow = String::from(&quot;Hello&quot;);
</span><span class="boring"> let borrowed = &amp;to_borrow;
</span><span class="boring"> let res = borrowed.len();
</span><span class="boring"> *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
</span><span class="boring">
</span><span class="boring"> // We set the self-reference here
</span><span class="boring"> if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
</span><span class="boring"> *borrowed = to_borrow;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> GeneratorState::Yielded(res)
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> GeneratorA::Yield1 {borrowed, ..} =&gt; {
</span><span class="boring"> let borrowed: &amp;String = unsafe {&amp;**borrowed};
</span><span class="boring"> println!(&quot;{} world&quot;, borrowed);
</span><span class="boring"> *self = GeneratorA::Exit;
</span><span class="boring"> GeneratorState::Complete(())
</span><span class="boring"> }
</span><span class="boring"> GeneratorA::Exit =&gt; panic!(&quot;Can't advance an exited generator!&quot;),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Complete(R),
# }
#
# trait Generator {
# type Yield;
# type Return;
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
# }
#
# enum GeneratorA {
# Enter,
# Yield1 {
# to_borrow: String,
# borrowed: *const String,
# },
# Exit,
# }
#
# impl GeneratorA {
# fn start() -&gt; Self {
# GeneratorA::Enter
# }
# }
# impl Generator for GeneratorA {
# type Yield = usize;
# type Return = ();
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt; {
# match self {
# GeneratorA::Enter =&gt; {
# let to_borrow = String::from(&quot;Hello&quot;);
# let borrowed = &amp;to_borrow;
# let res = borrowed.len();
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
#
# // We set the self-reference here
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
# *borrowed = to_borrow;
# }
#
# GeneratorState::Yielded(res)
# }
#
# GeneratorA::Yield1 {borrowed, ..} =&gt; {
# let borrowed: &amp;String = unsafe {&amp;**borrowed};
# println!(&quot;{} world&quot;, borrowed);
# *self = GeneratorA::Exit;
# GeneratorState::Complete(())
# }
# GeneratorA::Exit =&gt; panic!(&quot;Can't advance an exited generator!&quot;),
# }
# }
# }
</code></pre></pre>
<p>The problem is that in safe Rust we can still do this:</p>
<p><em>Run the code and compare the results. Do you see the problem?</em></p>
<pre><pre class="playpen"><code class="language-rust should_panic"><span class="boring">#![feature(never_type)] // Force nightly compiler to be used in playground
</span><span class="boring">// by betting on it's true that this type is named after it's stabilization date...
</span>pub fn main() {
<pre><pre class="playpen"><code class="language-rust should_panic"># #![feature(never_type)] // Force nightly compiler to be used in playground
# // by betting on it's true that this type is named after it's stabilization date...
pub fn main() {
let mut gen = GeneratorA::start();
let mut gen2 = GeneratorA::start();
@@ -589,61 +584,61 @@ does what we'd expect. But there is still one huge problem with this:</p>
()
};
}
<span class="boring">enum GeneratorState&lt;Y, R&gt; {
</span><span class="boring"> Yielded(Y),
</span><span class="boring"> Complete(R),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">trait Generator {
</span><span class="boring"> type Yield;
</span><span class="boring"> type Return;
</span><span class="boring"> fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum GeneratorA {
</span><span class="boring"> Enter,
</span><span class="boring"> Yield1 {
</span><span class="boring"> to_borrow: String,
</span><span class="boring"> borrowed: *const String,
</span><span class="boring"> },
</span><span class="boring"> Exit,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl GeneratorA {
</span><span class="boring"> fn start() -&gt; Self {
</span><span class="boring"> GeneratorA::Enter
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">impl Generator for GeneratorA {
</span><span class="boring"> type Yield = usize;
</span><span class="boring"> type Return = ();
</span><span class="boring"> fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt; {
</span><span class="boring"> match self {
</span><span class="boring"> GeneratorA::Enter =&gt; {
</span><span class="boring"> let to_borrow = String::from(&quot;Hello&quot;);
</span><span class="boring"> let borrowed = &amp;to_borrow;
</span><span class="boring"> let res = borrowed.len();
</span><span class="boring"> *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
</span><span class="boring">
</span><span class="boring"> // We set the self-reference here
</span><span class="boring"> if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
</span><span class="boring"> *borrowed = to_borrow;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> GeneratorState::Yielded(res)
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> GeneratorA::Yield1 {borrowed, ..} =&gt; {
</span><span class="boring"> let borrowed: &amp;String = unsafe {&amp;**borrowed};
</span><span class="boring"> println!(&quot;{} world&quot;, borrowed);
</span><span class="boring"> *self = GeneratorA::Exit;
</span><span class="boring"> GeneratorState::Complete(())
</span><span class="boring"> }
</span><span class="boring"> GeneratorA::Exit =&gt; panic!(&quot;Can't advance an exited generator!&quot;),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# enum GeneratorState&lt;Y, R&gt; {
# Yielded(Y),
# Complete(R),
# }
#
# trait Generator {
# type Yield;
# type Return;
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt;;
# }
#
# enum GeneratorA {
# Enter,
# Yield1 {
# to_borrow: String,
# borrowed: *const String,
# },
# Exit,
# }
#
# impl GeneratorA {
# fn start() -&gt; Self {
# GeneratorA::Enter
# }
# }
# impl Generator for GeneratorA {
# type Yield = usize;
# type Return = ();
# fn resume(&amp;mut self) -&gt; GeneratorState&lt;Self::Yield, Self::Return&gt; {
# match self {
# GeneratorA::Enter =&gt; {
# let to_borrow = String::from(&quot;Hello&quot;);
# let borrowed = &amp;to_borrow;
# let res = borrowed.len();
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
#
# // We set the self-reference here
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
# *borrowed = to_borrow;
# }
#
# GeneratorState::Yielded(res)
# }
#
# GeneratorA::Yield1 {borrowed, ..} =&gt; {
# let borrowed: &amp;String = unsafe {&amp;**borrowed};
# println!(&quot;{} world&quot;, borrowed);
# *self = GeneratorA::Exit;
# GeneratorState::Complete(())
# }
# GeneratorA::Exit =&gt; panic!(&quot;Can't advance an exited generator!&quot;),
# }
# }
# }
</code></pre></pre>
<p>Wait? What happened to &quot;Hello&quot;? And why did our code segfault?</p>
<p>Turns out that while the example above compiles just fine, we expose consumers
of this this API to both possible undefined behavior and other memory errors
@@ -810,18 +805,6 @@ pub fn main() {
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -167,7 +164,9 @@
your head around in the start, but once you unlock a mental model for it
it gets significantly easier to reason about.</p>
<h2><a class="header" href="#definitions" id="definitions">Definitions</a></h2>
<p>Pin is only relevant for pointers. A reference to an object is a pointer.</p>
<p>Pin wraps a pointer. A reference to an object is a pointer. Pin gives some
guarantees about the <em>pointee</em> (the data it points to) which we'll explore further
in this chapter.</p>
<p>Pin consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Pin's purpose in life is
to govern the rules that need to apply for types which implement <code>!Unpin</code>.</p>
<p>Yep, you're right, that's double negation right there. <code>!Unpin</code> means
@@ -189,7 +188,7 @@ just have to get used to them and use them as is.</p>
<a href="https://internals.rust-lang.org/t/naming-pin-anchor-move/6864/12">internals thread</a>.</p>
<h2><a class="header" href="#pinning-and-self-referential-structs" id="pinning-and-self-referential-structs">Pinning and self-referential structs</a></h2>
<p>Let's start where we left off in the last chapter by making the problem we
saw using a self-referential struct in our generator a lot simpler by making
saw using a self-references in our generator a lot simpler by making
some self-referential structs that are easier to reason about than our
state machines:</p>
<p>For now our example will look like this:</p>
@@ -242,45 +241,44 @@ you see, this works as expected:</p>
println!(&quot;a: {}, b: {}&quot;, test2.a(), test2.b());
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> let a = String::from(txt);
</span><span class="boring"> Test {
</span><span class="boring"> a,
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> // We need an `init` method to actually set our self-reference
</span><span class="boring"> fn init(&amp;mut self) {
</span><span class="boring"> let self_ref: *const String = &amp;self.a;
</span><span class="boring"> self.b = self_ref;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a(&amp;self) -&gt; &amp;str {
</span><span class="boring"> &amp;self.a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b(&amp;self) -&gt; &amp;String {
</span><span class="boring"> unsafe {&amp;*(self.b)}
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# }
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# let a = String::from(txt);
# Test {
# a,
# b: std::ptr::null(),
# }
# }
#
# // We need an `init` method to actually set our self-reference
# fn init(&amp;mut self) {
# let self_ref: *const String = &amp;self.a;
# self.b = self_ref;
# }
#
# fn a(&amp;self) -&gt; &amp;str {
# &amp;self.a
# }
#
# fn b(&amp;self) -&gt; &amp;String {
# unsafe {&amp;*(self.b)}
# }
# }
</code></pre></pre>
<p>In our main method we first instantiate two instances of <code>Test</code> and print out
the value of the fields on <code>test1</code>. We get what we'd expect:</p>
<pre><code class="language-rust ignore">a: test1, b: test1
a: test2, b: test2
</code></pre>
<p>Let's see what happens if we swap the data stored at the memory location
which <code>test1</code> is pointing to with the data stored at the memory location
<code>test2</code> is pointing to and vice a versa.</p>
<p>Let's see what happens if we swap the data stored at the memory location <code>test1</code> with the
data stored at the memory location <code>test2</code> and vice a versa.</p>
<pre><pre class="playpen"><code class="language-rust">fn main() {
let mut test1 = Test::new(&quot;test1&quot;);
test1.init();
@@ -292,36 +290,36 @@ which <code>test1</code> is pointing to with the data stored at the memory locat
println!(&quot;a: {}, b: {}&quot;, test2.a(), test2.b());
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> let a = String::from(txt);
</span><span class="boring"> Test {
</span><span class="boring"> a,
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn init(&amp;mut self) {
</span><span class="boring"> let self_ref: *const String = &amp;self.a;
</span><span class="boring"> self.b = self_ref;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a(&amp;self) -&gt; &amp;str {
</span><span class="boring"> &amp;self.a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b(&amp;self) -&gt; &amp;String {
</span><span class="boring"> unsafe {&amp;*(self.b)}
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# }
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# let a = String::from(txt);
# Test {
# a,
# b: std::ptr::null(),
# }
# }
#
# fn init(&amp;mut self) {
# let self_ref: *const String = &amp;self.a;
# self.b = self_ref;
# }
#
# fn a(&amp;self) -&gt; &amp;str {
# &amp;self.a
# }
#
# fn b(&amp;self) -&gt; &amp;String {
# unsafe {&amp;*(self.b)}
# }
# }
</code></pre></pre>
<p>Naively, we could think that what we should get a debug print of <code>test1</code> two
times like this</p>
<pre><code class="language-rust ignore">a: test1, b: test1
@@ -348,36 +346,36 @@ be tied to the lifetime of <code>test2</code> anymore.</p>
println!(&quot;a: {}, b: {}&quot;, test2.a(), test2.b());
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> let a = String::from(txt);
</span><span class="boring"> Test {
</span><span class="boring"> a,
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn init(&amp;mut self) {
</span><span class="boring"> let self_ref: *const String = &amp;self.a;
</span><span class="boring"> self.b = self_ref;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a(&amp;self) -&gt; &amp;str {
</span><span class="boring"> &amp;self.a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b(&amp;self) -&gt; &amp;String {
</span><span class="boring"> unsafe {&amp;*(self.b)}
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# }
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# let a = String::from(txt);
# Test {
# a,
# b: std::ptr::null(),
# }
# }
#
# fn init(&amp;mut self) {
# let self_ref: *const String = &amp;self.a;
# self.b = self_ref;
# }
#
# fn a(&amp;self) -&gt; &amp;str {
# &amp;self.a
# }
#
# fn b(&amp;self) -&gt; &amp;String {
# unsafe {&amp;*(self.b)}
# }
# }
</code></pre></pre>
<p>That shouldn't happen. There is no serious error yet, but as you can imagine
it's easy to create serious bugs using this code.</p>
<p>I created a diagram to help visualize what's going on:</p>
@@ -423,7 +421,7 @@ impl Test {
}
}
</code></pre>
<p>Now, what we've done here is pinning a stack address. That will always be
<p>Now, what we've done here is pinning an object to the stack. That will always be
<code>unsafe</code> if our type implements <code>!Unpin</code>.</p>
<p>We use the same tricks here, including requiring an <code>init</code>. If we want to fix that
and let users avoid <code>unsafe</code> we need to pin our data on the heap instead which
@@ -443,42 +441,42 @@ we'll show in a second.</p>
println!(&quot;a: {}, b: {}&quot;, Test::a(test1.as_ref()), Test::b(test1.as_ref()));
println!(&quot;a: {}, b: {}&quot;, Test::a(test2.as_ref()), Test::b(test2.as_ref()));
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">use std::marker::PhantomPinned;
</span><span class="boring">
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring"> _marker: PhantomPinned,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> let a = String::from(txt);
</span><span class="boring"> Test {
</span><span class="boring"> a,
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> // This makes our type `!Unpin`
</span><span class="boring"> _marker: PhantomPinned,
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
</span><span class="boring"> let self_ptr: *const String = &amp;self.a;
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
</span><span class="boring"> this.b = self_ptr;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
</span><span class="boring"> &amp;self.get_ref().a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
</span><span class="boring"> unsafe { &amp;*(self.b) }
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# use std::marker::PhantomPinned;
#
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# _marker: PhantomPinned,
# }
#
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# let a = String::from(txt);
# Test {
# a,
# b: std::ptr::null(),
# // This makes our type `!Unpin`
# _marker: PhantomPinned,
# }
# }
# fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
# let self_ptr: *const String = &amp;self.a;
# let this = unsafe { self.get_unchecked_mut() };
# this.b = self_ptr;
# }
#
# fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
# &amp;self.get_ref().a
# }
#
# fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
# unsafe { &amp;*(self.b) }
# }
# }
</code></pre></pre>
<p>Now, if we try to pull the same trick which got us in to trouble the last time
you'll get a compilation error.</p>
<pre><pre class="playpen"><code class="language-rust compile_fail">pub fn main() {
@@ -494,50 +492,50 @@ you'll get a compilation error.</p>
std::mem::swap(test1.get_mut(), test2.get_mut());
println!(&quot;a: {}, b: {}&quot;, Test::a(test2.as_ref()), Test::b(test2.as_ref()));
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">use std::marker::PhantomPinned;
</span><span class="boring">
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring"> _marker: PhantomPinned,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> Test {
</span><span class="boring"> a: let a = String::from(txt),
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> _marker: PhantomPinned, // This makes our type `!Unpin`
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
</span><span class="boring"> let self_ptr: *const String = &amp;self.a;
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
</span><span class="boring"> this.b = self_ptr;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
</span><span class="boring"> &amp;self.get_ref().a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
</span><span class="boring"> unsafe { &amp;*(self.b) }
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# use std::marker::PhantomPinned;
#
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# _marker: PhantomPinned,
# }
#
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# Test {
# a: let a = String::from(txt),
# b: std::ptr::null(),
# _marker: PhantomPinned, // This makes our type `!Unpin`
# }
# }
# fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
# let self_ptr: *const String = &amp;self.a;
# let this = unsafe { self.get_unchecked_mut() };
# this.b = self_ptr;
# }
#
# fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
# &amp;self.get_ref().a
# }
#
# fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
# unsafe { &amp;*(self.b) }
# }
# }
</code></pre></pre>
<p>As you see from the error you get by running the code the type system prevents
us from swapping the pinned pointers.</p>
<blockquote>
<p>It's important to note that stack pinning will always depend on the current
stack frame we're in, so we can't create a self referential object in one
stack frame and return it since any pointers we take to &quot;self&quot; is invalidated.</p>
<p>It also puts a lot of responsibility in your hands if you pin a value to the
<p>It also puts a lot of responsibility in your hands if you pin an object to the
stack. A mistake that is easy to make is, forgetting to shadow the original variable
since you could drop the pinned pointer and access the old value
after it's initialized like this:</p>
since you could drop the <code>Pin</code> and access the old value after it's initialized
like this:</p>
<pre><pre class="playpen"><code class="language-rust">fn main() {
let mut test1 = Test::new(&quot;test1&quot;);
let mut test1_pin = unsafe { Pin::new_unchecked(&amp;mut test1) };
@@ -548,41 +546,41 @@ after it's initialized like this:</p>
mem::swap(&amp;mut test1, &amp;mut test2);
println!(&quot;Not self referential anymore: {:?}&quot;, test1.b);
}
<span class="boring">use std::pin::Pin;
</span><span class="boring">use std::marker::PhantomPinned;
</span><span class="boring">use std::mem;
</span><span class="boring">
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Test {
</span><span class="boring"> a: String,
</span><span class="boring"> b: *const String,
</span><span class="boring"> _marker: PhantomPinned,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">
</span><span class="boring">impl Test {
</span><span class="boring"> fn new(txt: &amp;str) -&gt; Self {
</span><span class="boring"> Test {
</span><span class="boring"> a: String::from(txt),
</span><span class="boring"> b: std::ptr::null(),
</span><span class="boring"> _marker: PhantomPinned, // This makes our type `!Unpin`
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
</span><span class="boring"> let self_ptr: *const String = &amp;self.a;
</span><span class="boring"> let this = unsafe { self.get_unchecked_mut() };
</span><span class="boring"> this.b = self_ptr;
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
</span><span class="boring"> &amp;self.get_ref().a
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
</span><span class="boring"> unsafe { &amp;*(self.b) }
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# use std::pin::Pin;
# use std::marker::PhantomPinned;
# use std::mem;
#
# #[derive(Debug)]
# struct Test {
# a: String,
# b: *const String,
# _marker: PhantomPinned,
# }
#
#
# impl Test {
# fn new(txt: &amp;str) -&gt; Self {
# Test {
# a: String::from(txt),
# b: std::ptr::null(),
# _marker: PhantomPinned, // This makes our type `!Unpin`
# }
# }
# fn init&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) {
# let self_ptr: *const String = &amp;self.a;
# let this = unsafe { self.get_unchecked_mut() };
# this.b = self_ptr;
# }
#
# fn a&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a str {
# &amp;self.get_ref().a
# }
#
# fn b&lt;'a&gt;(self: Pin&lt;&amp;'a Self&gt;) -&gt; &amp;'a String {
# unsafe { &amp;*(self.b) }
# }
# }
</code></pre></pre>
</blockquote>
<h2><a class="header" href="#pinning-to-the-heap" id="pinning-to-the-heap">Pinning to the heap</a></h2>
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
@@ -629,7 +627,7 @@ pub fn main() {
println!(&quot;a: {}, b: {}&quot;,test2.as_ref().a(), test2.as_ref().b());
}
</code></pre></pre>
<p>The fact that it's safe to pin a heap allocated value even if it is <code>!Unpin</code>
<p>The fact that it's safe to pin heap allocated data even if it is <code>!Unpin</code>
makes sense. Once the data is allocated on the heap it will have a stable address.</p>
<p>There is no need for us as users of the API to take special care and ensure
that the self-referential pointer stays valid.</p>
@@ -660,8 +658,7 @@ exceptions.</p>
</li>
<li>
<p>The main use case for <code>Pin</code> is to allow self referential types, the whole
justification for stabilizing them was to allow that. There are still corner
cases in the API which are being explored.</p>
justification for stabilizing them was to allow that.</p>
</li>
<li>
<p>The implementation behind objects that are <code>!Unpin</code> is most likely unsafe.
@@ -674,13 +671,13 @@ this book, creating and reading fields of a self referential struct still requir
by adding <code>std::marker::PhantomPinned</code> to your type on stable.</p>
</li>
<li>
<p>You can either pin a value to memory on the stack or on the heap.</p>
<p>You can either pin an object to the stack or to the heap.</p>
</li>
<li>
<p>Pinning a <code>!Unpin</code> pointer to the stack requires <code>unsafe</code></p>
<p>Pinning a <code>!Unpin</code> object to the stack requires <code>unsafe</code></p>
</li>
<li>
<p>Pinning a <code>!Unpin</code> pointer to the heap does not require <code>unsafe</code>. There is a shortcut for doing this using <code>Box::pin</code>.</p>
<p>Pinning a <code>!Unpin</code> object to the heap does not require <code>unsafe</code>. There is a shortcut for doing this using <code>Box::pin</code>.</p>
</li>
</ol>
<blockquote>
@@ -709,11 +706,11 @@ use std::pin::Pin;
pub fn main() {
let gen1 = GeneratorA::start();
let gen2 = GeneratorA::start();
// Before we pin the pointers, this is safe to do
// Before we pin the data, this is safe to do
// std::mem::swap(&amp;mut gen, &amp;mut gen2);
// constructing a `Pin::new()` on a type which does not implement `Unpin` is
// unsafe. A value pinned to heap can be constructed while staying in safe
// unsafe. An object pinned to heap can be constructed while staying in safe
// Rust so we can use that to avoid unsafe. You can also use crates like
// `pin_utils` to pin to the stack safely, just remember that they use
// unsafe under the hood so it's like using an already-reviewed unsafe
@@ -770,9 +767,9 @@ impl GeneratorA {
}
}
// This tells us that the underlying pointer is not safe to move after pinning.
// This tells us that this object is not safe to move after pinning.
// In this case, only we as implementors &quot;feel&quot; this, however, if someone is
// relying on our Pinned pointer this will prevent them from moving it. You need
// relying on our Pinned data this will prevent them from moving it. You need
// to enable the feature flag `#![feature(optin_builtin_traits)]` and use the
// nightly compiler to implement `!Unpin`. Normally, you would use
// `std::marker::PhantomPinned` to indicate that the struct is `!Unpin`.
@@ -898,18 +895,6 @@ we want to be able to safely borrow across <code>yield/await</code> points.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html" class="active"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html" class="active"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -382,7 +379,7 @@ trouble of creating our own <code>vtable</code> and a <code>RawWaker</code>. We
a normal trait.</p>
<p>Fortunately, in the future this will probably be possible in the standard
library as well. For now, <a href="https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/task/trait.ArcWake.html">this trait lives in the nursery</a>, but my
guess is that this will be a part of the standard library after som maturing.</p>
guess is that this will be a part of the standard library after some maturing.</p>
<p>We choose to pass in a reference to the whole <code>Reactor</code> here. This isn't normal.
The reactor will often be a global resource which let's us register interests
without passing around a reference.</p>
@@ -561,13 +558,13 @@ and make it sleep for some time which we specify when we create a <code>Task</co
of seconds here, just give it some time to run.</p>
<p>In the last chapter we have the <a href="./8_finished_example.html">whole 200 lines in an editable window</a>
which you can edit and change the way you like.</p>
<pre><pre class="playpen"><code class="language-rust edition2018"><span class="boring">use std::{
</span><span class="boring"> future::Future, pin::Pin, sync::{ mpsc::{channel, Sender}, Arc, Mutex,},
</span><span class="boring"> task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, mem,
</span><span class="boring"> thread::{self, JoinHandle}, time::{Duration, Instant}, collections::HashMap
</span><span class="boring">};
</span><span class="boring">
</span>fn main() {
<pre><pre class="playpen"><code class="language-rust edition2018"># use std::{
# future::Future, pin::Pin, sync::{ mpsc::{channel, Sender}, Arc, Mutex,},
# task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, mem,
# thread::{self, JoinHandle}, time::{Duration, Instant}, collections::HashMap
# };
#
fn main() {
// This is just to make it easier for us to see when our Future was resolved
let start = Instant::now();
@@ -608,177 +605,177 @@ which you can edit and change the way you like.</p>
// ends nicely.
reactor.lock().map(|mut r| r.close()).unwrap();
}
<span class="boring">// ============================= EXECUTOR ====================================
</span><span class="boring">fn block_on&lt;F: Future&gt;(mut future: F) -&gt; F::Output {
</span><span class="boring"> let mywaker = Arc::new(MyWaker {
</span><span class="boring"> thread: thread::current(),
</span><span class="boring"> });
</span><span class="boring"> let waker = waker_into_waker(Arc::into_raw(mywaker));
</span><span class="boring"> let mut cx = Context::from_waker(&amp;waker);
</span><span class="boring">
</span><span class="boring"> // SAFETY: we shadow `future` so it can't be accessed again.
</span><span class="boring"> let mut future = unsafe { Pin::new_unchecked(&amp;mut future) };
</span><span class="boring"> let val = loop {
</span><span class="boring"> match Future::poll(future.as_mut(), &amp;mut cx) {
</span><span class="boring"> Poll::Ready(val) =&gt; break val,
</span><span class="boring"> Poll::Pending =&gt; thread::park(),
</span><span class="boring"> };
</span><span class="boring"> };
</span><span class="boring"> val
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">// ====================== FUTURE IMPLEMENTATION ==============================
</span><span class="boring">#[derive(Clone)]
</span><span class="boring">struct MyWaker {
</span><span class="boring"> thread: thread::Thread,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[derive(Clone)]
</span><span class="boring">pub struct Task {
</span><span class="boring"> id: usize,
</span><span class="boring"> reactor: Arc&lt;Mutex&lt;Box&lt;Reactor&gt;&gt;&gt;,
</span><span class="boring"> data: u64,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn mywaker_wake(s: &amp;MyWaker) {
</span><span class="boring"> let waker_ptr: *const MyWaker = s;
</span><span class="boring"> let waker_arc = unsafe { Arc::from_raw(waker_ptr) };
</span><span class="boring"> waker_arc.thread.unpark();
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn mywaker_clone(s: &amp;MyWaker) -&gt; RawWaker {
</span><span class="boring"> let arc = unsafe { Arc::from_raw(s) };
</span><span class="boring"> std::mem::forget(arc.clone()); // increase ref count
</span><span class="boring"> RawWaker::new(Arc::into_raw(arc) as *const (), &amp;VTABLE)
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">const VTABLE: RawWakerVTable = unsafe {
</span><span class="boring"> RawWakerVTable::new(
</span><span class="boring"> |s| mywaker_clone(&amp;*(s as *const MyWaker)), // clone
</span><span class="boring"> |s| mywaker_wake(&amp;*(s as *const MyWaker)), // wake
</span><span class="boring"> |s| mywaker_wake(*(s as *const &amp;MyWaker)), // wake by ref
</span><span class="boring"> |s| drop(Arc::from_raw(s as *const MyWaker)), // decrease refcount
</span><span class="boring"> )
</span><span class="boring">};
</span><span class="boring">
</span><span class="boring">fn waker_into_waker(s: *const MyWaker) -&gt; Waker {
</span><span class="boring"> let raw_waker = RawWaker::new(s as *const (), &amp;VTABLE);
</span><span class="boring"> unsafe { Waker::from_raw(raw_waker) }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Task {
</span><span class="boring"> fn new(reactor: Arc&lt;Mutex&lt;Box&lt;Reactor&gt;&gt;&gt;, data: u64, id: usize) -&gt; Self {
</span><span class="boring"> Task { id, reactor, data }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Future for Task {
</span><span class="boring"> type Output = usize;
</span><span class="boring"> fn poll(self: Pin&lt;&amp;mut Self&gt;, cx: &amp;mut Context&lt;'_&gt;) -&gt; Poll&lt;Self::Output&gt; {
</span><span class="boring"> let mut r = self.reactor.lock().unwrap();
</span><span class="boring"> if r.is_ready(self.id) {
</span><span class="boring"> println!(&quot;POLL: TASK {} IS READY&quot;, self.id);
</span><span class="boring"> *r.tasks.get_mut(&amp;self.id).unwrap() = TaskState::Finished;
</span><span class="boring"> Poll::Ready(self.id)
</span><span class="boring"> } else if r.tasks.contains_key(&amp;self.id) {
</span><span class="boring"> println!(&quot;POLL: REPLACED WAKER FOR TASK: {}&quot;, self.id);
</span><span class="boring"> r.tasks.insert(self.id, TaskState::NotReady(cx.waker().clone()));
</span><span class="boring"> Poll::Pending
</span><span class="boring"> } else {
</span><span class="boring"> println!(&quot;POLL: REGISTERED TASK: {}, WAKER: {:?}&quot;, self.id, cx.waker());
</span><span class="boring"> r.register(self.data, cx.waker().clone(), self.id);
</span><span class="boring"> Poll::Pending
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">// =============================== REACTOR ===================================
</span><span class="boring">enum TaskState {
</span><span class="boring"> Ready,
</span><span class="boring"> NotReady(Waker),
</span><span class="boring"> Finished,
</span><span class="boring">}
</span><span class="boring">struct Reactor {
</span><span class="boring"> dispatcher: Sender&lt;Event&gt;,
</span><span class="boring"> handle: Option&lt;JoinHandle&lt;()&gt;&gt;,
</span><span class="boring"> tasks: HashMap&lt;usize, TaskState&gt;,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">#[derive(Debug)]
</span><span class="boring">enum Event {
</span><span class="boring"> Close,
</span><span class="boring"> Timeout(u64, usize),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Reactor {
</span><span class="boring"> fn new() -&gt; Arc&lt;Mutex&lt;Box&lt;Self&gt;&gt;&gt; {
</span><span class="boring"> let (tx, rx) = channel::&lt;Event&gt;();
</span><span class="boring"> let reactor = Arc::new(Mutex::new(Box::new(Reactor {
</span><span class="boring"> dispatcher: tx,
</span><span class="boring"> handle: None,
</span><span class="boring"> tasks: HashMap::new(),
</span><span class="boring"> })));
</span><span class="boring">
</span><span class="boring"> let reactor_clone = Arc::downgrade(&amp;reactor);
</span><span class="boring"> let handle = thread::spawn(move || {
</span><span class="boring"> let mut handles = vec![];
</span><span class="boring"> // This simulates some I/O resource
</span><span class="boring"> for event in rx {
</span><span class="boring"> println!(&quot;REACTOR: {:?}&quot;, event);
</span><span class="boring"> let reactor = reactor_clone.clone();
</span><span class="boring"> match event {
</span><span class="boring"> Event::Close =&gt; break,
</span><span class="boring"> Event::Timeout(duration, id) =&gt; {
</span><span class="boring"> let event_handle = thread::spawn(move || {
</span><span class="boring"> thread::sleep(Duration::from_secs(duration));
</span><span class="boring"> let reactor = reactor.upgrade().unwrap();
</span><span class="boring"> reactor.lock().map(|mut r| r.wake(id)).unwrap();
</span><span class="boring"> });
</span><span class="boring"> handles.push(event_handle);
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring"> handles.into_iter().for_each(|handle| handle.join().unwrap());
</span><span class="boring"> });
</span><span class="boring"> reactor.lock().map(|mut r| r.handle = Some(handle)).unwrap();
</span><span class="boring"> reactor
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn wake(&amp;mut self, id: usize) {
</span><span class="boring"> self.tasks.get_mut(&amp;id).map(|state| {
</span><span class="boring"> match mem::replace(state, TaskState::Ready) {
</span><span class="boring"> TaskState::NotReady(waker) =&gt; waker.wake(),
</span><span class="boring"> TaskState::Finished =&gt; panic!(&quot;Called 'wake' twice on task: {}&quot;, id),
</span><span class="boring"> _ =&gt; unreachable!()
</span><span class="boring"> }
</span><span class="boring"> }).unwrap();
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn register(&amp;mut self, duration: u64, waker: Waker, id: usize) {
</span><span class="boring"> if self.tasks.insert(id, TaskState::NotReady(waker)).is_some() {
</span><span class="boring"> panic!(&quot;Tried to insert a task with id: '{}', twice!&quot;, id);
</span><span class="boring"> }
</span><span class="boring"> self.dispatcher.send(Event::Timeout(duration, id)).unwrap();
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn close(&amp;mut self) {
</span><span class="boring"> self.dispatcher.send(Event::Close).unwrap();
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> fn is_ready(&amp;self, id: usize) -&gt; bool {
</span><span class="boring"> self.tasks.get(&amp;id).map(|state| match state {
</span><span class="boring"> TaskState::Ready =&gt; true,
</span><span class="boring"> _ =&gt; false,
</span><span class="boring"> }).unwrap_or(false)
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Drop for Reactor {
</span><span class="boring"> fn drop(&amp;mut self) {
</span><span class="boring"> self.handle.take().map(|h| h.join().unwrap()).unwrap();
</span><span class="boring"> }
</span><span class="boring">}
</span></code></pre></pre>
# // ============================= EXECUTOR ====================================
# fn block_on&lt;F: Future&gt;(mut future: F) -&gt; F::Output {
# let mywaker = Arc::new(MyWaker {
# thread: thread::current(),
# });
# let waker = waker_into_waker(Arc::into_raw(mywaker));
# let mut cx = Context::from_waker(&amp;waker);
#
# // SAFETY: we shadow `future` so it can't be accessed again.
# let mut future = unsafe { Pin::new_unchecked(&amp;mut future) };
# let val = loop {
# match Future::poll(future.as_mut(), &amp;mut cx) {
# Poll::Ready(val) =&gt; break val,
# Poll::Pending =&gt; thread::park(),
# };
# };
# val
# }
#
# // ====================== FUTURE IMPLEMENTATION ==============================
# #[derive(Clone)]
# struct MyWaker {
# thread: thread::Thread,
# }
#
# #[derive(Clone)]
# pub struct Task {
# id: usize,
# reactor: Arc&lt;Mutex&lt;Box&lt;Reactor&gt;&gt;&gt;,
# data: u64,
# }
#
# fn mywaker_wake(s: &amp;MyWaker) {
# let waker_ptr: *const MyWaker = s;
# let waker_arc = unsafe { Arc::from_raw(waker_ptr) };
# waker_arc.thread.unpark();
# }
#
# fn mywaker_clone(s: &amp;MyWaker) -&gt; RawWaker {
# let arc = unsafe { Arc::from_raw(s) };
# std::mem::forget(arc.clone()); // increase ref count
# RawWaker::new(Arc::into_raw(arc) as *const (), &amp;VTABLE)
# }
#
# const VTABLE: RawWakerVTable = unsafe {
# RawWakerVTable::new(
# |s| mywaker_clone(&amp;*(s as *const MyWaker)), // clone
# |s| mywaker_wake(&amp;*(s as *const MyWaker)), // wake
# |s| mywaker_wake(*(s as *const &amp;MyWaker)), // wake by ref
# |s| drop(Arc::from_raw(s as *const MyWaker)), // decrease refcount
# )
# };
#
# fn waker_into_waker(s: *const MyWaker) -&gt; Waker {
# let raw_waker = RawWaker::new(s as *const (), &amp;VTABLE);
# unsafe { Waker::from_raw(raw_waker) }
# }
#
# impl Task {
# fn new(reactor: Arc&lt;Mutex&lt;Box&lt;Reactor&gt;&gt;&gt;, data: u64, id: usize) -&gt; Self {
# Task { id, reactor, data }
# }
# }
#
# impl Future for Task {
# type Output = usize;
# fn poll(self: Pin&lt;&amp;mut Self&gt;, cx: &amp;mut Context&lt;'_&gt;) -&gt; Poll&lt;Self::Output&gt; {
# let mut r = self.reactor.lock().unwrap();
# if r.is_ready(self.id) {
# println!(&quot;POLL: TASK {} IS READY&quot;, self.id);
# *r.tasks.get_mut(&amp;self.id).unwrap() = TaskState::Finished;
# Poll::Ready(self.id)
# } else if r.tasks.contains_key(&amp;self.id) {
# println!(&quot;POLL: REPLACED WAKER FOR TASK: {}&quot;, self.id);
# r.tasks.insert(self.id, TaskState::NotReady(cx.waker().clone()));
# Poll::Pending
# } else {
# println!(&quot;POLL: REGISTERED TASK: {}, WAKER: {:?}&quot;, self.id, cx.waker());
# r.register(self.data, cx.waker().clone(), self.id);
# Poll::Pending
# }
# }
# }
#
# // =============================== REACTOR ===================================
# enum TaskState {
# Ready,
# NotReady(Waker),
# Finished,
# }
# struct Reactor {
# dispatcher: Sender&lt;Event&gt;,
# handle: Option&lt;JoinHandle&lt;()&gt;&gt;,
# tasks: HashMap&lt;usize, TaskState&gt;,
# }
#
# #[derive(Debug)]
# enum Event {
# Close,
# Timeout(u64, usize),
# }
#
# impl Reactor {
# fn new() -&gt; Arc&lt;Mutex&lt;Box&lt;Self&gt;&gt;&gt; {
# let (tx, rx) = channel::&lt;Event&gt;();
# let reactor = Arc::new(Mutex::new(Box::new(Reactor {
# dispatcher: tx,
# handle: None,
# tasks: HashMap::new(),
# })));
#
# let reactor_clone = Arc::downgrade(&amp;reactor);
# let handle = thread::spawn(move || {
# let mut handles = vec![];
# // This simulates some I/O resource
# for event in rx {
# println!(&quot;REACTOR: {:?}&quot;, event);
# let reactor = reactor_clone.clone();
# match event {
# Event::Close =&gt; break,
# Event::Timeout(duration, id) =&gt; {
# let event_handle = thread::spawn(move || {
# thread::sleep(Duration::from_secs(duration));
# let reactor = reactor.upgrade().unwrap();
# reactor.lock().map(|mut r| r.wake(id)).unwrap();
# });
# handles.push(event_handle);
# }
# }
# }
# handles.into_iter().for_each(|handle| handle.join().unwrap());
# });
# reactor.lock().map(|mut r| r.handle = Some(handle)).unwrap();
# reactor
# }
#
# fn wake(&amp;mut self, id: usize) {
# self.tasks.get_mut(&amp;id).map(|state| {
# match mem::replace(state, TaskState::Ready) {
# TaskState::NotReady(waker) =&gt; waker.wake(),
# TaskState::Finished =&gt; panic!(&quot;Called 'wake' twice on task: {}&quot;, id),
# _ =&gt; unreachable!()
# }
# }).unwrap();
# }
#
# fn register(&amp;mut self, duration: u64, waker: Waker, id: usize) {
# if self.tasks.insert(id, TaskState::NotReady(waker)).is_some() {
# panic!(&quot;Tried to insert a task with id: '{}', twice!&quot;, id);
# }
# self.dispatcher.send(Event::Timeout(duration, id)).unwrap();
# }
#
# fn close(&amp;mut self) {
# self.dispatcher.send(Event::Close).unwrap();
# }
#
# fn is_ready(&amp;self, id: usize) -&gt; bool {
# self.tasks.get(&amp;id).map(|state| match state {
# TaskState::Ready =&gt; true,
# _ =&gt; false,
# }).unwrap_or(false)
# }
# }
#
# impl Drop for Reactor {
# fn drop(&amp;mut self) {
# self.handle.take().map(|h| h.join().unwrap()).unwrap();
# }
# }
</code></pre></pre>
<p>I added a some debug printouts so we can observe a couple of things:</p>
<ol>
<li>How the <code>Waker</code> object looks just like the <em>trait object</em> we talked about in an earlier chapter</li>
@@ -981,18 +978,6 @@ improvement which you can explore further if you wish.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html" class="active"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html" class="active"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -436,18 +433,6 @@ impl Drop for Reactor {
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -16,6 +16,9 @@ function playpen_text(playpen) {
}
(function codeSnippets() {
// Hide Rust code lines prepended with a specific character
var hiding_character = "#";
function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([
fetch(url, options),
@@ -52,15 +55,6 @@ function playpen_text(playpen) {
editor.addEventListener("change", function (e) {
update_play_button(playpen_block, playground_crates);
});
// add Ctrl-Enter command to execute rust code
editor.commands.addCommand({
name: "run",
bindKey: {
win: "Ctrl-Enter",
mac: "Ctrl-Enter"
},
exec: _editor => run_rust_code(playpen_block)
});
}
}
}
@@ -167,59 +161,95 @@ function playpen_text(playpen) {
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
var lines = Array.from(block.querySelectorAll('.boring'));
var code_block = block;
var pre_block = block.parentNode;
// hide lines
var lines = code_block.innerHTML.split("\n");
var first_non_hidden_line = false;
var lines_hidden = false;
var trimmed_line = "";
for (var n = 0; n < lines.length; n++) {
trimmed_line = lines[n].trim();
if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) {
if (first_non_hidden_line) {
lines[n] = "<span class=\"hidden\">" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + "</span>";
}
else {
lines[n] = "<span class=\"hidden\">" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + "</span>";
}
lines_hidden = true;
}
else if (first_non_hidden_line) {
lines[n] = "\n" + lines[n];
}
else {
first_non_hidden_line = true;
}
if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) {
lines[n] = lines[n].replace("##", "#")
}
}
code_block.innerHTML = lines.join("");
// If no lines were hidden, return
if (!lines.length) { return; }
block.classList.add("hide-boring");
if (!lines_hidden) { return; }
var buttons = document.createElement('div');
buttons.className = 'buttons';
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
// add expand button
var pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
if (e.target.classList.contains('fa-expand')) {
var lines = pre_block.querySelectorAll('span.hidden');
e.target.classList.remove('fa-expand');
e.target.classList.add('fa-compress');
e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title);
block.classList.remove('hide-boring');
Array.from(lines).forEach(function (line) {
line.classList.remove('hidden');
line.classList.add('unhidden');
});
} else if (e.target.classList.contains('fa-compress')) {
var lines = pre_block.querySelectorAll('span.unhidden');
e.target.classList.remove('fa-compress');
e.target.classList.add('fa-expand');
e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title);
block.classList.add('hide-boring');
Array.from(lines).forEach(function (line) {
line.classList.remove('unhidden');
line.classList.add('hidden');
});
}
});
});
if (window.playpen_copyable) {
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
var pre_block = block.parentNode;
if (!pre_block.classList.contains('playpen')) {
var buttons = pre_block.querySelector(".buttons");
if (!buttons) {
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
var clipButton = document.createElement('button');
clipButton.className = 'fa fa-copy clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
var pre_block = block.parentNode;
if (!pre_block.classList.contains('playpen')) {
var buttons = pre_block.querySelector(".buttons");
if (!buttons) {
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
});
}
var clipButton = document.createElement('button');
clipButton.className = 'fa fa-copy clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
}
});
// Process playpen code blocks
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
@@ -237,21 +267,19 @@ function playpen_text(playpen) {
runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title);
var copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(runCodeButton, buttons.firstChild);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
runCodeButton.addEventListener('click', function (e) {
run_rust_code(pre_block);
});
if (window.playpen_copyable) {
var copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
}
let code_block = pre_block.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
var undoChangesButton = document.createElement('button');
@@ -293,7 +321,7 @@ function playpen_text(playpen) {
themeToggleButton.focus();
}
function set_theme(theme, store = true) {
function set_theme(theme) {
let ace_theme;
if (theme == 'coal' || theme == 'navy') {
@@ -328,10 +356,9 @@ function playpen_text(playpen) {
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
if (store) {
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
}
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
document.body.className = theme;
html.classList.remove(previousTheme);
html.classList.add(theme);
}
@@ -341,7 +368,7 @@ function playpen_text(playpen) {
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
set_theme(theme, false);
set_theme(theme);
themeToggleButton.addEventListener('click', function () {
if (themePopup.style.display === 'block') {
@@ -363,7 +390,7 @@ function playpen_text(playpen) {
}
});
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang-nursery/mdBook/issues/628
document.addEventListener('click', function(e) {
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
hideThemes();
@@ -408,7 +435,6 @@ function playpen_text(playpen) {
(function sidebar() {
var html = document.querySelector("html");
var sidebar = document.getElementById("sidebar");
var sidebarScrollBox = document.getElementById("sidebar-scrollbox");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
@@ -425,17 +451,6 @@ function playpen_text(playpen) {
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
}
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
function hideSidebar() {
html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden');
@@ -507,7 +522,7 @@ function playpen_text(playpen) {
// Scroll sidebar to current active section
var activeSection = sidebar.querySelector(".active");
if (activeSection) {
sidebarScrollBox.scrollTop = activeSection.offsetTop;
sidebar.scrollTop = activeSection.offsetTop;
}
})();
@@ -600,6 +615,6 @@ function playpen_text(playpen) {
menu.classList.remove('bordered');
}
previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0);
previousScrollTop = document.scrollingElement.scrollTop;
}, { passive: true });
})();

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html" class="active">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html" class="active">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -168,8 +165,9 @@ some good exercises if you want to explore more:</p>
<p>First of all, protecting the whole <code>Reactor</code> and passing it around is overkill. We're only
interested in synchronizing some parts of the information it contains. Try to refactor that
out and only synchronize access to what's really needed.</p>
<p>I'd encourage you to have a look at how <a href="https://github.com/async-rs/async-std/blob/master/src/net/driver/mod.rs">the async_std driver is implemented</a>
and <a href="https://github.com/tokio-rs/tokio/blob/master/tokio/src/runtime/basic_scheduler.rs">how tokios scheduler is implemented</a> to get some inspiration.</p>
<p>I'd encourage you to have a look at how async_std solves this with a global <a href="https://github.com/async-rs/async-std/blob/b446cd023084a157b8a531cff65b8df37750be58/src/rt/mod.rs#L15-L23">runtime</a> which includes the <a href="https://github.com/async-rs/async-std/blob/b446cd023084a157b8a531cff65b8df37750be58/src/rt/runtime.rs#L39-L70">reactor</a>
and <a href="https://github.com/tokio-rs/tokio/blob/19a87e090ed528001e0363a30f6165304a710d49/tokio/src/runtime/context.rs#L2-L12">how tokio's rutime</a> solves the same
thing in a slightly different way to get some inspiration.</p>
<ul>
<li>Do you want to pass around a reference to this information using an <code>Arc</code>?</li>
<li>Do you want to make a global <code>Reactor</code> so it can be accessed from anywhere?</li>
@@ -258,18 +256,6 @@ linked to in the book, here are some of my suggestions:</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -8,9 +8,7 @@
::-webkit-scrollbar-thumb {
background: var(--scrollbar);
}
html {
scrollbar-color: var(--scrollbar) var(--bg);
}
#searchresults a,
.content a:link,
a:visited,
@@ -45,7 +43,7 @@ a > .hljs {
position: relative;
padding: 0 8px;
z-index: 10;
line-height: var(--menu-bar-height);
line-height: 50px;
cursor: pointer;
transition: color 0.5s;
}
@@ -73,7 +71,7 @@ a > .hljs {
}
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
transform: translateY(calc(-10px - var(--menu-bar-height)));
transform: translateY(-60px);
}
.left-buttons {
@@ -87,8 +85,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
.menu-title {
display: inline-block;
font-weight: 200;
font-size: 2rem;
line-height: var(--menu-bar-height);
font-size: 20px;
line-height: 50px;
text-align: center;
margin: 0;
flex: 1;
@@ -126,7 +124,7 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
text-decoration: none;
position: fixed;
top: 0;
top: 50px; /* Height of menu-bar */
bottom: 0;
margin: 0;
max-width: 150px;
@@ -137,14 +135,10 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
align-content: center;
flex-direction: column;
transition: color 0.5s, background-color 0.5s;
transition: color 0.5s;
}
.nav-chapters:hover {
text-decoration: none;
background-color: var(--theme-hover);
transition: background-color 0.15s, color 0.15s;
}
.nav-chapters:hover { text-decoration: none; }
.nav-wrapper {
margin-top: 50px;
@@ -182,7 +176,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
/* Inline code */
:not(pre) > .hljs {
display: inline;
display: inline-block;
vertical-align: middle;
padding: 0.1em 0.3em;
border-radius: 3px;
}
@@ -375,13 +370,7 @@ ul#searchresults span.teaser em {
padding-left: 0;
line-height: 2.2em;
}
.chapter ol {
width: 100%;
}
.chapter li {
display: flex;
color: var(--sidebar-non-existant);
}
.chapter li a {
@@ -395,32 +384,10 @@ ul#searchresults span.teaser em {
color: var(--sidebar-active);
}
.chapter li a.active {
.chapter li .active {
color: var(--sidebar-active);
}
.chapter li > a.toggle {
cursor: pointer;
display: block;
margin-left: auto;
padding: 0 10px;
user-select: none;
opacity: 0.68;
}
.chapter li > a.toggle div {
transition: transform 0.5s;
}
/* collapse the section */
.chapter li:not(.expanded) + li > ol {
display: none;
}
.chapter li.expanded > a.toggle div {
transform: rotate(90deg);
}
.spacer {
width: 100%;
height: 3px;
@@ -446,7 +413,7 @@ ul#searchresults span.teaser em {
.theme-popup {
position: absolute;
left: 10px;
top: var(--menu-bar-height);
top: 50px;
z-index: 1000;
border-radius: 4px;
font-size: 0.7em;

View File

@@ -2,11 +2,6 @@
@import 'variables.css';
:root {
/* Browser default font-size is 16px, this way 1 rem = 10px */
font-size: 62.5%;
}
html {
font-family: "Open Sans", sans-serif;
color: var(--fg);
@@ -16,20 +11,19 @@ html {
body {
margin: 0;
font-size: 1.6rem;
font-size: 1rem;
overflow-x: hidden;
}
code {
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
}
.left { float: left; }
.right { float: right; }
.boring { opacity: 0.6; }
.hide-boring .boring { display: none; }
.hidden { display: none; }
.play-button.hidden { display: none; }
h2, h3 { margin-top: 2.5em; }
h4, h5 { margin-top: 2em; }
@@ -50,13 +44,6 @@ h4 a.header:target::before {
width: 30px;
}
h1 a.header:target,
h2 a.header:target,
h3 a.header:target,
h4 a.header:target {
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}
.page {
outline: 0;
padding: 0 var(--page-padding);
@@ -105,9 +92,6 @@ table thead td {
font-weight: 700;
border: none;
}
table thead th {
padding: 3px 20px;
}
table thead tr {
border: 1px var(--table-header-bg) solid;
}

View File

@@ -5,7 +5,6 @@
--sidebar-width: 300px;
--page-padding: 15px;
--content-max-width: 750px;
--menu-bar-height: 50px;
}
/* Themes */
@@ -209,45 +208,3 @@
--searchresults-li-bg: #dec2a2;
--search-mark-bg: #e69f67;
}
@media (prefers-color-scheme: dark) {
.light.no-js {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
}
}

View File

@@ -6,14 +6,12 @@ window.editors = [];
}
Array.from(document.querySelectorAll('.editable')).forEach(function(editable) {
let display_line_numbers = window.playpen_line_numbers || false;
let editor = ace.edit(editable);
editor.setOptions({
highlightActiveLine: false,
showPrintMargin: false,
showLineNumbers: display_line_numbers,
showGutter: display_line_numbers,
showLineNumbers: false,
showGutter: false,
maxLines: Infinity,
fontSize: "0.875em" // please adjust the font size of the code in general.css
});

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -214,10 +211,6 @@ read the finished product, but a big thanks is definitely due.</p>
<a rel="next" href="0_background_information.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>
@@ -228,10 +221,6 @@ read the finished product, but a big thanks is definitely due.</p>
<a href="0_background_information.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>
@@ -273,18 +262,6 @@ read the finished product, but a big thanks is definitely due.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head>
<body>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="introduction.html" class="active">Introduction</a></li><li class="expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="expanded "><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="expanded "><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li class="expanded "><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li class="expanded "><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li class="expanded "><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li class="expanded "><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html" class="active">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -273,18 +270,6 @@ read the finished product, but a big thanks is definitely due.</p>
<script type="text/javascript">
window.playpen_line_numbers = true;
</script>
<script type="text/javascript">
window.playpen_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long