redesign
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -148,7 +145,77 @@
|
|||||||
|
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1>
|
<h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
|
||||||
|
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||||
|
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||||
|
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||||
|
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||||
|
and then gradually adapt our example so it implements all these concepts, and
|
||||||
|
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||||
|
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||||
|
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||||
|
example yourself.</p>
|
||||||
|
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||||
|
<p>That's a valid question. There are many good resources and examples already. First
|
||||||
|
of all, this book will point you to some background information that I have found
|
||||||
|
very valuable to get an understanding of concurrent programming in general.</p>
|
||||||
|
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||||
|
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||||
|
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||||
|
all code that you can download, play with and learn from.</p>
|
||||||
|
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||||
|
<p><strong>We'll:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||||
|
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||||
|
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||||
|
<li>Make sure all examples can be run on the playground</li>
|
||||||
|
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>We'll not:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||||
|
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||||
|
better off if you read this before you go research them</li>
|
||||||
|
<li>Explain concurrent programming, but I will supply sources</li>
|
||||||
|
</ul>
|
||||||
|
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||||
|
book.</p>
|
||||||
|
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||||
|
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||||
|
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||||
|
much is built upon. Reading and exploring some of this code is nothing less than
|
||||||
|
impressive.</p>
|
||||||
|
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||||
|
<p>Well, I think it has to do with several things:</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>Futures has a very interesting implementation, compiling down to a state
|
||||||
|
machine using generators to suspend and resume execution. In a language such as
|
||||||
|
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||||
|
if this complexity when working with futures and want to understand them, not
|
||||||
|
only learn how to use them.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||||
|
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||||
|
too difficult, you need to make more choices than you need in GO and other
|
||||||
|
languages designed with a concurrent programming in mind and ships with a
|
||||||
|
runtime.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||||
|
known to have some issues regarding ergonomics. Turns out that modelling
|
||||||
|
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||||
|
and type signatures with a type system as Rust.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||||
|
<ol start="4">
|
||||||
|
<li>Async await syntax was recently stabilized</li>
|
||||||
|
</ol>
|
||||||
|
<p>what we'll
|
||||||
|
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -180,30 +247,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
215
book/0_1_1_trait_objects.html
Normal file
215
book/0_1_1_trait_objects.html
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Trait objects and fat pointers - Futures Explained in 200 Lines of Rust</title>
|
||||||
|
|
||||||
|
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="favicon.png">
|
||||||
|
<link rel="stylesheet" href="css/variables.css">
|
||||||
|
<link rel="stylesheet" href="css/general.css">
|
||||||
|
<link rel="stylesheet" href="css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="highlight.css">
|
||||||
|
<link rel="stylesheet" href="tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var path_to_root = "";
|
||||||
|
var default_theme = "light";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<div class="sidebar-scrollbox">
|
||||||
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html" class="active"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
|
</div>
|
||||||
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Futures Explained in 200 Lines of Rust</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<h1><a class="header" href="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="0_1_background_information.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="0_1_2_generators_pin.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="0_1_background_information.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="0_1_2_generators_pin.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script 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>
|
||||||
|
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
215
book/0_1_2_generators_pin.html
Normal file
215
book/0_1_2_generators_pin.html
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
|
<head>
|
||||||
|
<!-- Book generated using mdBook -->
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Generators and Pin - Futures Explained in 200 Lines of Rust</title>
|
||||||
|
|
||||||
|
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
|
<link rel="shortcut icon" href="favicon.png">
|
||||||
|
<link rel="stylesheet" href="css/variables.css">
|
||||||
|
<link rel="stylesheet" href="css/general.css">
|
||||||
|
<link rel="stylesheet" href="css/chrome.css">
|
||||||
|
<link rel="stylesheet" href="css/print.css" media="print">
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<!-- Highlight.js Stylesheets -->
|
||||||
|
<link rel="stylesheet" href="highlight.css">
|
||||||
|
<link rel="stylesheet" href="tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme stylesheets -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="light">
|
||||||
|
<!-- Provide site root to javascript -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var path_to_root = "";
|
||||||
|
var default_theme = "light";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
try {
|
||||||
|
var theme = localStorage.getItem('mdbook-theme');
|
||||||
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||||
|
|
||||||
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||||
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||||
|
}
|
||||||
|
} catch (e) { }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
|
document.body.className = theme;
|
||||||
|
document.querySelector('html').className = theme + ' js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var sidebar = 'hidden';
|
||||||
|
if (document.body.clientWidth >= 1080) {
|
||||||
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||||
|
sidebar = sidebar || 'visible';
|
||||||
|
}
|
||||||
|
html.classList.remove('sidebar-visible');
|
||||||
|
html.classList.add("sidebar-" + sidebar);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
|
<div class="sidebar-scrollbox">
|
||||||
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html" class="active"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
|
</div>
|
||||||
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="page-wrapper" class="page-wrapper">
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<div id="menu-bar" class="menu-bar">
|
||||||
|
<div id="menu-bar-sticky-container">
|
||||||
|
<div class="left-buttons">
|
||||||
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||||
|
<i class="fa fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||||
|
<i class="fa fa-paint-brush"></i>
|
||||||
|
</button>
|
||||||
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||||
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||||
|
<i class="fa fa-search"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="menu-title">Futures Explained in 200 Lines of Rust</h1>
|
||||||
|
|
||||||
|
<div class="right-buttons">
|
||||||
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
<i id="print-button" class="fa fa-print"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="search-wrapper" class="hidden">
|
||||||
|
<form id="searchbar-outer" class="searchbar-outer">
|
||||||
|
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||||
|
</form>
|
||||||
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||||
|
<div id="searchresults-header" class="searchresults-header"></div>
|
||||||
|
<ul id="searchresults">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||||
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||||
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||||
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="content" class="content">
|
||||||
|
<main>
|
||||||
|
<h1><a class="header" href="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
|
<a rel="prev" href="0_1_1_trait_objects.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a rel="next" href="0_2_naive_implementation.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="clear: both"></div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
|
<a href="0_1_1_trait_objects.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
|
<i class="fa fa-angle-left"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="0_2_naive_implementation.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
|
<i class="fa fa-angle-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script 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>
|
||||||
|
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS scripts -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -173,8 +170,8 @@ object we construct our selves.</p>
|
|||||||
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
||||||
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
||||||
run the following code:</p>
|
run the following code:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"><span class="boring">use std::mem::size_of;
|
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
|
||||||
</span>trait SomeTrait { }
|
trait SomeTrait { }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||||
@@ -339,7 +336,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="0_2_naive_implementation.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a rel="next" href="0_1_1_trait_objects.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -357,7 +354,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="0_2_naive_implementation.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
<a href="0_1_1_trait_objects.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||||
<i class="fa fa-angle-right"></i>
|
<i class="fa fa-angle-right"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -366,30 +363,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html" class="active"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html" class="active"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -297,125 +294,125 @@ fn main() {
|
|||||||
|
|
||||||
executor_run(reactor, readylist);
|
executor_run(reactor, readylist);
|
||||||
}
|
}
|
||||||
<span class="boring">// ====== EXECUTOR ======
|
# // ====== EXECUTOR ======
|
||||||
</span><span class="boring">fn executor_run(mut reactor: Reactor, rl: Arc<Mutex<Vec<usize>>>) {
|
# fn executor_run(mut reactor: Reactor, rl: Arc<Mutex<Vec<usize>>>) {
|
||||||
</span><span class="boring"> let start = Instant::now();
|
# let start = Instant::now();
|
||||||
</span><span class="boring"> loop {
|
# loop {
|
||||||
</span><span class="boring"> let mut rl_locked = rl.lock().unwrap();
|
# let mut rl_locked = rl.lock().unwrap();
|
||||||
</span><span class="boring"> while let Some(event) = rl_locked.pop() {
|
# while let Some(event) = rl_locked.pop() {
|
||||||
</span><span class="boring"> let dur = (Instant::now() - start).as_secs_f32();
|
# let dur = (Instant::now() - start).as_secs_f32();
|
||||||
</span><span class="boring"> println!("Event {} just happened at time: {:.2}.", event, dur);
|
# println!("Event {} just happened at time: {:.2}.", event, dur);
|
||||||
</span><span class="boring"> reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
|
# reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> drop(rl_locked);
|
# drop(rl_locked);
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> if reactor.outstanding.load(Ordering::Relaxed) == 0 {
|
# if reactor.outstanding.load(Ordering::Relaxed) == 0 {
|
||||||
</span><span class="boring"> reactor.close();
|
# reactor.close();
|
||||||
</span><span class="boring"> break;
|
# break;
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> thread::park();
|
# thread::park();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">// ====== "FUTURE" IMPL ======
|
# // ====== "FUTURE" IMPL ======
|
||||||
</span><span class="boring">#[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
</span><span class="boring">struct MyWaker {
|
# struct MyWaker {
|
||||||
</span><span class="boring"> id: usize,
|
# id: usize,
|
||||||
</span><span class="boring"> thread: thread::Thread,
|
# thread: thread::Thread,
|
||||||
</span><span class="boring"> readylist: Arc<Mutex<Vec<usize>>>,
|
# readylist: Arc<Mutex<Vec<usize>>>,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl MyWaker {
|
# impl MyWaker {
|
||||||
</span><span class="boring"> fn new(id: usize, thread: thread::Thread, readylist: Arc<Mutex<Vec<usize>>>) -> Self {
|
# fn new(id: usize, thread: thread::Thread, readylist: Arc<Mutex<Vec<usize>>>) -> Self {
|
||||||
</span><span class="boring"> MyWaker {
|
# MyWaker {
|
||||||
</span><span class="boring"> id,
|
# id,
|
||||||
</span><span class="boring"> thread,
|
# thread,
|
||||||
</span><span class="boring"> readylist,
|
# readylist,
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn wake(&self) {
|
# fn wake(&self) {
|
||||||
</span><span class="boring"> self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
|
# self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
|
||||||
</span><span class="boring"> self.thread.unpark();
|
# self.thread.unpark();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">#[derive(Debug, Clone)]
|
# #[derive(Debug, Clone)]
|
||||||
</span><span class="boring">pub struct Task {
|
# pub struct Task {
|
||||||
</span><span class="boring"> id: usize,
|
# id: usize,
|
||||||
</span><span class="boring"> pending: bool,
|
# pending: bool,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">// ===== REACTOR =====
|
# // ===== REACTOR =====
|
||||||
</span><span class="boring">struct Reactor {
|
# struct Reactor {
|
||||||
</span><span class="boring"> dispatcher: Sender<Event>,
|
# dispatcher: Sender<Event>,
|
||||||
</span><span class="boring"> handle: Option<JoinHandle<()>>,
|
# handle: Option<JoinHandle<()>>,
|
||||||
</span><span class="boring"> outstanding: AtomicUsize,
|
# outstanding: AtomicUsize,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">#[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
</span><span class="boring">enum Event {
|
# enum Event {
|
||||||
</span><span class="boring"> Close,
|
# Close,
|
||||||
</span><span class="boring"> Simple(MyWaker, u64),
|
# Simple(MyWaker, u64),
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl Reactor {
|
# impl Reactor {
|
||||||
</span><span class="boring"> fn new() -> Self {
|
# fn new() -> Self {
|
||||||
</span><span class="boring"> let (tx, rx) = channel::<Event>();
|
# let (tx, rx) = channel::<Event>();
|
||||||
</span><span class="boring"> let mut handles = vec![];
|
# let mut handles = vec![];
|
||||||
</span><span class="boring"> let handle = thread::spawn(move || {
|
# let handle = thread::spawn(move || {
|
||||||
</span><span class="boring"> // This simulates some I/O resource
|
# // This simulates some I/O resource
|
||||||
</span><span class="boring"> for event in rx {
|
# for event in rx {
|
||||||
</span><span class="boring"> match event {
|
# match event {
|
||||||
</span><span class="boring"> Event::Close => break,
|
# Event::Close => break,
|
||||||
</span><span class="boring"> Event::Simple(mywaker, duration) => {
|
# Event::Simple(mywaker, duration) => {
|
||||||
</span><span class="boring"> let event_handle = thread::spawn(move || {
|
# let event_handle = thread::spawn(move || {
|
||||||
</span><span class="boring"> thread::sleep(Duration::from_secs(duration));
|
# thread::sleep(Duration::from_secs(duration));
|
||||||
</span><span class="boring"> mywaker.wake();
|
# mywaker.wake();
|
||||||
</span><span class="boring"> });
|
# });
|
||||||
</span><span class="boring"> handles.push(event_handle);
|
# handles.push(event_handle);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> for handle in handles {
|
# for handle in handles {
|
||||||
</span><span class="boring"> handle.join().unwrap();
|
# handle.join().unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> });
|
# });
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> Reactor {
|
# Reactor {
|
||||||
</span><span class="boring"> dispatcher: tx,
|
# dispatcher: tx,
|
||||||
</span><span class="boring"> handle: Some(handle),
|
# handle: Some(handle),
|
||||||
</span><span class="boring"> outstanding: AtomicUsize::new(0),
|
# outstanding: AtomicUsize::new(0),
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn register(&mut self, duration: u64, mywaker: MyWaker) {
|
# fn register(&mut self, duration: u64, mywaker: MyWaker) {
|
||||||
</span><span class="boring"> self.dispatcher
|
# self.dispatcher
|
||||||
</span><span class="boring"> .send(Event::Simple(mywaker, duration))
|
# .send(Event::Simple(mywaker, duration))
|
||||||
</span><span class="boring"> .unwrap();
|
# .unwrap();
|
||||||
</span><span class="boring"> self.outstanding.fetch_add(1, Ordering::Relaxed);
|
# self.outstanding.fetch_add(1, Ordering::Relaxed);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn close(&mut self) {
|
# fn close(&mut self) {
|
||||||
</span><span class="boring"> self.dispatcher.send(Event::Close).unwrap();
|
# self.dispatcher.send(Event::Close).unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl Drop for Reactor {
|
# impl Drop for Reactor {
|
||||||
</span><span class="boring"> fn drop(&mut self) {
|
# fn drop(&mut self) {
|
||||||
</span><span class="boring"> self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
# self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span></code></pre></pre>
|
</code></pre></pre>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||||
<!-- Mobile navigation buttons -->
|
<!-- Mobile navigation buttons -->
|
||||||
|
|
||||||
<a rel="prev" href="0_1_background_information.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a rel="prev" href="0_1_2_generators_pin.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -433,7 +430,7 @@ fn main() {
|
|||||||
|
|
||||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||||
|
|
||||||
<a href="0_1_background_information.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
<a href="0_1_2_generators_pin.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||||
<i class="fa fa-angle-left"></i>
|
<i class="fa fa-angle-left"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -448,30 +445,7 @@ fn main() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html" class="active"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html" class="active"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -188,30 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html" class="active"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html" class="active"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -188,30 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html" class="active"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html" class="active"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -188,30 +185,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html" class="active"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html" class="active"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -180,30 +177,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
101
book/book.js
101
book/book.js
@@ -16,6 +16,9 @@ function playpen_text(playpen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(function codeSnippets() {
|
(function codeSnippets() {
|
||||||
|
// Hide Rust code lines prepended with a specific character
|
||||||
|
var hiding_character = "#";
|
||||||
|
|
||||||
function fetch_with_timeout(url, options, timeout = 6000) {
|
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
fetch(url, options),
|
fetch(url, options),
|
||||||
@@ -52,15 +55,6 @@ function playpen_text(playpen) {
|
|||||||
editor.addEventListener("change", function (e) {
|
editor.addEventListener("change", function (e) {
|
||||||
update_play_button(playpen_block, playground_crates);
|
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,39 +161,76 @@ function playpen_text(playpen) {
|
|||||||
|
|
||||||
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
|
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 no lines were hidden, return
|
||||||
if (!lines.length) { return; }
|
if (!lines_hidden) { return; }
|
||||||
block.classList.add("hide-boring");
|
|
||||||
|
|
||||||
var buttons = document.createElement('div');
|
var buttons = document.createElement('div');
|
||||||
buttons.className = 'buttons';
|
buttons.className = 'buttons';
|
||||||
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||||
|
|
||||||
// add expand button
|
// add expand button
|
||||||
var pre_block = block.parentNode;
|
|
||||||
pre_block.insertBefore(buttons, pre_block.firstChild);
|
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||||
|
|
||||||
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
|
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
|
||||||
if (e.target.classList.contains('fa-expand')) {
|
if (e.target.classList.contains('fa-expand')) {
|
||||||
|
var lines = pre_block.querySelectorAll('span.hidden');
|
||||||
|
|
||||||
e.target.classList.remove('fa-expand');
|
e.target.classList.remove('fa-expand');
|
||||||
e.target.classList.add('fa-compress');
|
e.target.classList.add('fa-compress');
|
||||||
e.target.title = 'Hide lines';
|
e.target.title = 'Hide lines';
|
||||||
e.target.setAttribute('aria-label', e.target.title);
|
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')) {
|
} else if (e.target.classList.contains('fa-compress')) {
|
||||||
|
var lines = pre_block.querySelectorAll('span.unhidden');
|
||||||
|
|
||||||
e.target.classList.remove('fa-compress');
|
e.target.classList.remove('fa-compress');
|
||||||
e.target.classList.add('fa-expand');
|
e.target.classList.add('fa-expand');
|
||||||
e.target.title = 'Show hidden lines';
|
e.target.title = 'Show hidden lines';
|
||||||
e.target.setAttribute('aria-label', e.target.title);
|
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) {
|
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
|
||||||
var pre_block = block.parentNode;
|
var pre_block = block.parentNode;
|
||||||
if (!pre_block.classList.contains('playpen')) {
|
if (!pre_block.classList.contains('playpen')) {
|
||||||
@@ -219,7 +250,6 @@ function playpen_text(playpen) {
|
|||||||
buttons.insertBefore(clipButton, buttons.firstChild);
|
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Process playpen code blocks
|
// Process playpen code blocks
|
||||||
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
|
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
|
||||||
@@ -237,20 +267,18 @@ function playpen_text(playpen) {
|
|||||||
runCodeButton.title = 'Run this code';
|
runCodeButton.title = 'Run this code';
|
||||||
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||||
|
|
||||||
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
|
||||||
runCodeButton.addEventListener('click', function (e) {
|
|
||||||
run_rust_code(pre_block);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (window.playpen_copyable) {
|
|
||||||
var copyCodeClipboardButton = document.createElement('button');
|
var copyCodeClipboardButton = document.createElement('button');
|
||||||
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||||
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||||
copyCodeClipboardButton.title = 'Copy to clipboard';
|
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||||
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||||
|
|
||||||
|
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||||
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||||
}
|
|
||||||
|
runCodeButton.addEventListener('click', function (e) {
|
||||||
|
run_rust_code(pre_block);
|
||||||
|
});
|
||||||
|
|
||||||
let code_block = pre_block.querySelector("code");
|
let code_block = pre_block.querySelector("code");
|
||||||
if (window.ace && code_block.classList.contains("editable")) {
|
if (window.ace && code_block.classList.contains("editable")) {
|
||||||
@@ -293,7 +321,7 @@ function playpen_text(playpen) {
|
|||||||
themeToggleButton.focus();
|
themeToggleButton.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_theme(theme, store = true) {
|
function set_theme(theme) {
|
||||||
let ace_theme;
|
let ace_theme;
|
||||||
|
|
||||||
if (theme == 'coal' || theme == 'navy') {
|
if (theme == 'coal' || theme == 'navy') {
|
||||||
@@ -328,10 +356,9 @@ function playpen_text(playpen) {
|
|||||||
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||||
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
|
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.remove(previousTheme);
|
||||||
html.classList.add(theme);
|
html.classList.add(theme);
|
||||||
}
|
}
|
||||||
@@ -341,7 +368,7 @@ function playpen_text(playpen) {
|
|||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
|
|
||||||
set_theme(theme, false);
|
set_theme(theme);
|
||||||
|
|
||||||
themeToggleButton.addEventListener('click', function () {
|
themeToggleButton.addEventListener('click', function () {
|
||||||
if (themePopup.style.display === 'block') {
|
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) {
|
document.addEventListener('click', function(e) {
|
||||||
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
|
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
|
||||||
hideThemes();
|
hideThemes();
|
||||||
@@ -408,7 +435,6 @@ function playpen_text(playpen) {
|
|||||||
(function sidebar() {
|
(function sidebar() {
|
||||||
var html = document.querySelector("html");
|
var html = document.querySelector("html");
|
||||||
var sidebar = document.getElementById("sidebar");
|
var sidebar = document.getElementById("sidebar");
|
||||||
var sidebarScrollBox = document.getElementById("sidebar-scrollbox");
|
|
||||||
var sidebarLinks = document.querySelectorAll('#sidebar a');
|
var sidebarLinks = document.querySelectorAll('#sidebar a');
|
||||||
var sidebarToggleButton = document.getElementById("sidebar-toggle");
|
var sidebarToggleButton = document.getElementById("sidebar-toggle");
|
||||||
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
|
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
|
||||||
@@ -425,17 +451,6 @@ function playpen_text(playpen) {
|
|||||||
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
|
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() {
|
function hideSidebar() {
|
||||||
html.classList.remove('sidebar-visible')
|
html.classList.remove('sidebar-visible')
|
||||||
html.classList.add('sidebar-hidden');
|
html.classList.add('sidebar-hidden');
|
||||||
@@ -507,7 +522,7 @@ function playpen_text(playpen) {
|
|||||||
// Scroll sidebar to current active section
|
// Scroll sidebar to current active section
|
||||||
var activeSection = sidebar.querySelector(".active");
|
var activeSection = sidebar.querySelector(".active");
|
||||||
if (activeSection) {
|
if (activeSection) {
|
||||||
sidebarScrollBox.scrollTop = activeSection.offsetTop;
|
sidebar.scrollTop = activeSection.offsetTop;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -600,6 +615,6 @@ function playpen_text(playpen) {
|
|||||||
menu.classList.remove('bordered');
|
menu.classList.remove('bordered');
|
||||||
}
|
}
|
||||||
|
|
||||||
previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
previousScrollTop = document.scrollingElement.scrollTop;
|
||||||
}, { passive: true });
|
}, { passive: true });
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: var(--scrollbar);
|
background: var(--scrollbar);
|
||||||
}
|
}
|
||||||
html {
|
|
||||||
scrollbar-color: var(--scrollbar) var(--bg);
|
|
||||||
}
|
|
||||||
#searchresults a,
|
#searchresults a,
|
||||||
.content a:link,
|
.content a:link,
|
||||||
a:visited,
|
a:visited,
|
||||||
@@ -45,7 +43,7 @@ a > .hljs {
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
line-height: var(--menu-bar-height);
|
line-height: 50px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.5s;
|
transition: color 0.5s;
|
||||||
}
|
}
|
||||||
@@ -73,7 +71,7 @@ a > .hljs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
|
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 {
|
.left-buttons {
|
||||||
@@ -87,8 +85,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
|||||||
.menu-title {
|
.menu-title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
font-size: 2rem;
|
font-size: 20px;
|
||||||
line-height: var(--menu-bar-height);
|
line-height: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -126,7 +124,7 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 50px; /* Height of menu-bar */
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
@@ -137,14 +135,10 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
|||||||
align-content: center;
|
align-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
transition: color 0.5s, background-color 0.5s;
|
transition: color 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-chapters:hover {
|
.nav-chapters:hover { text-decoration: none; }
|
||||||
text-decoration: none;
|
|
||||||
background-color: var(--theme-hover);
|
|
||||||
transition: background-color 0.15s, color 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-wrapper {
|
.nav-wrapper {
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
@@ -182,7 +176,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
|
|||||||
/* Inline code */
|
/* Inline code */
|
||||||
|
|
||||||
:not(pre) > .hljs {
|
:not(pre) > .hljs {
|
||||||
display: inline;
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
padding: 0.1em 0.3em;
|
padding: 0.1em 0.3em;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
@@ -375,13 +370,7 @@ ul#searchresults span.teaser em {
|
|||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
line-height: 2.2em;
|
line-height: 2.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chapter ol {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chapter li {
|
.chapter li {
|
||||||
display: flex;
|
|
||||||
color: var(--sidebar-non-existant);
|
color: var(--sidebar-non-existant);
|
||||||
}
|
}
|
||||||
.chapter li a {
|
.chapter li a {
|
||||||
@@ -395,32 +384,10 @@ ul#searchresults span.teaser em {
|
|||||||
color: var(--sidebar-active);
|
color: var(--sidebar-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chapter li a.active {
|
.chapter li .active {
|
||||||
color: var(--sidebar-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 {
|
.spacer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
@@ -446,7 +413,7 @@ ul#searchresults span.teaser em {
|
|||||||
.theme-popup {
|
.theme-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
top: var(--menu-bar-height);
|
top: 50px;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
@import 'variables.css';
|
@import 'variables.css';
|
||||||
|
|
||||||
:root {
|
|
||||||
/* Browser default font-size is 16px, this way 1 rem = 10px */
|
|
||||||
font-size: 62.5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: "Open Sans", sans-serif;
|
font-family: "Open Sans", sans-serif;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
@@ -16,20 +11,19 @@ html {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.6rem;
|
font-size: 1rem;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
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 */
|
font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
|
||||||
}
|
}
|
||||||
|
|
||||||
.left { float: left; }
|
.left { float: left; }
|
||||||
.right { float: right; }
|
.right { float: right; }
|
||||||
.boring { opacity: 0.6; }
|
|
||||||
.hide-boring .boring { display: none; }
|
|
||||||
.hidden { display: none; }
|
.hidden { display: none; }
|
||||||
|
.play-button.hidden { display: none; }
|
||||||
|
|
||||||
h2, h3 { margin-top: 2.5em; }
|
h2, h3 { margin-top: 2.5em; }
|
||||||
h4, h5 { margin-top: 2em; }
|
h4, h5 { margin-top: 2em; }
|
||||||
@@ -50,13 +44,6 @@ h4 a.header:target::before {
|
|||||||
width: 30px;
|
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 {
|
.page {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
padding: 0 var(--page-padding);
|
padding: 0 var(--page-padding);
|
||||||
@@ -105,9 +92,6 @@ table thead td {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
table thead th {
|
|
||||||
padding: 3px 20px;
|
|
||||||
}
|
|
||||||
table thead tr {
|
table thead tr {
|
||||||
border: 1px var(--table-header-bg) solid;
|
border: 1px var(--table-header-bg) solid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
--sidebar-width: 300px;
|
--sidebar-width: 300px;
|
||||||
--page-padding: 15px;
|
--page-padding: 15px;
|
||||||
--content-max-width: 750px;
|
--content-max-width: 750px;
|
||||||
--menu-bar-height: 50px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Themes */
|
/* Themes */
|
||||||
@@ -209,45 +208,3 @@
|
|||||||
--searchresults-li-bg: #dec2a2;
|
--searchresults-li-bg: #dec2a2;
|
||||||
--search-mark-bg: #e69f67;
|
--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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ window.editors = [];
|
|||||||
}
|
}
|
||||||
|
|
||||||
Array.from(document.querySelectorAll('.editable')).forEach(function(editable) {
|
Array.from(document.querySelectorAll('.editable')).forEach(function(editable) {
|
||||||
let display_line_numbers = window.playpen_line_numbers || false;
|
|
||||||
|
|
||||||
let editor = ace.edit(editable);
|
let editor = ace.edit(editable);
|
||||||
editor.setOptions({
|
editor.setOptions({
|
||||||
highlightActiveLine: false,
|
highlightActiveLine: false,
|
||||||
showPrintMargin: false,
|
showPrintMargin: false,
|
||||||
showLineNumbers: display_line_numbers,
|
showLineNumbers: false,
|
||||||
showGutter: display_line_numbers,
|
showGutter: false,
|
||||||
maxLines: Infinity,
|
maxLines: Infinity,
|
||||||
fontSize: "0.875em" // please adjust the font size of the code in general.css
|
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
120
book/index.html
120
book/index.html
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -60,11 +60,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -80,8 +77,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -148,7 +145,77 @@
|
|||||||
|
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1>
|
<h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
|
||||||
|
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||||
|
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||||
|
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||||
|
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||||
|
and then gradually adapt our example so it implements all these concepts, and
|
||||||
|
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||||
|
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||||
|
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||||
|
example yourself.</p>
|
||||||
|
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||||
|
<p>That's a valid question. There are many good resources and examples already. First
|
||||||
|
of all, this book will point you to some background information that I have found
|
||||||
|
very valuable to get an understanding of concurrent programming in general.</p>
|
||||||
|
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||||
|
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||||
|
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||||
|
all code that you can download, play with and learn from.</p>
|
||||||
|
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||||
|
<p><strong>We'll:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||||
|
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||||
|
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||||
|
<li>Make sure all examples can be run on the playground</li>
|
||||||
|
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>We'll not:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||||
|
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||||
|
better off if you read this before you go research them</li>
|
||||||
|
<li>Explain concurrent programming, but I will supply sources</li>
|
||||||
|
</ul>
|
||||||
|
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||||
|
book.</p>
|
||||||
|
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||||
|
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||||
|
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||||
|
much is built upon. Reading and exploring some of this code is nothing less than
|
||||||
|
impressive.</p>
|
||||||
|
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||||
|
<p>Well, I think it has to do with several things:</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>Futures has a very interesting implementation, compiling down to a state
|
||||||
|
machine using generators to suspend and resume execution. In a language such as
|
||||||
|
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||||
|
if this complexity when working with futures and want to understand them, not
|
||||||
|
only learn how to use them.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||||
|
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||||
|
too difficult, you need to make more choices than you need in GO and other
|
||||||
|
languages designed with a concurrent programming in mind and ships with a
|
||||||
|
runtime.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||||
|
known to have some issues regarding ergonomics. Turns out that modelling
|
||||||
|
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||||
|
and type signatures with a type system as Rust.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||||
|
<ol start="4">
|
||||||
|
<li>Async await syntax was recently stabilized</li>
|
||||||
|
</ol>
|
||||||
|
<p>what we'll
|
||||||
|
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -157,10 +224,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a rel="next" href="0_1_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>
|
<div style="clear: both"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -171,39 +234,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a href="0_1_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>
|
</nav>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
342
book/print.html
342
book/print.html
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
<html lang="en" class="sidebar-visible no-js light">
|
<html lang="en" class="sidebar-visible no-js">
|
||||||
<head>
|
<head>
|
||||||
<!-- Book generated using mdBook -->
|
<!-- Book generated using mdBook -->
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -34,11 +34,11 @@
|
|||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="light">
|
||||||
<!-- Provide site root to javascript -->
|
<!-- Provide site root to javascript -->
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var path_to_root = "";
|
var path_to_root = "";
|
||||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
var default_theme = "light";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||||
@@ -62,11 +62,8 @@
|
|||||||
var theme;
|
var theme;
|
||||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||||
var html = document.querySelector('html');
|
document.body.className = theme;
|
||||||
html.classList.remove('no-js')
|
document.querySelector('html').className = theme + ' js';
|
||||||
html.classList.remove('light')
|
|
||||||
html.classList.add(theme);
|
|
||||||
html.classList.add('js');
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Hide / unhide sidebar before it is displayed -->
|
<!-- Hide / unhide sidebar before it is displayed -->
|
||||||
@@ -82,8 +79,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||||
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
|
<div class="sidebar-scrollbox">
|
||||||
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
|
||||||
</div>
|
</div>
|
||||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -150,7 +147,77 @@
|
|||||||
|
|
||||||
<div id="content" class="content">
|
<div id="content" class="content">
|
||||||
<main>
|
<main>
|
||||||
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1>
|
<h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
|
||||||
|
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||||
|
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||||
|
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||||
|
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||||
|
and then gradually adapt our example so it implements all these concepts, and
|
||||||
|
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||||
|
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||||
|
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||||
|
example yourself.</p>
|
||||||
|
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||||
|
<p>That's a valid question. There are many good resources and examples already. First
|
||||||
|
of all, this book will point you to some background information that I have found
|
||||||
|
very valuable to get an understanding of concurrent programming in general.</p>
|
||||||
|
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||||
|
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||||
|
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||||
|
all code that you can download, play with and learn from.</p>
|
||||||
|
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||||
|
<p><strong>We'll:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||||
|
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||||
|
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||||
|
<li>Make sure all examples can be run on the playground</li>
|
||||||
|
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>We'll not:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||||
|
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||||
|
better off if you read this before you go research them</li>
|
||||||
|
<li>Explain concurrent programming, but I will supply sources</li>
|
||||||
|
</ul>
|
||||||
|
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||||
|
book.</p>
|
||||||
|
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||||
|
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||||
|
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||||
|
much is built upon. Reading and exploring some of this code is nothing less than
|
||||||
|
impressive.</p>
|
||||||
|
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||||
|
<p>Well, I think it has to do with several things:</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
<p>Futures has a very interesting implementation, compiling down to a state
|
||||||
|
machine using generators to suspend and resume execution. In a language such as
|
||||||
|
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||||
|
if this complexity when working with futures and want to understand them, not
|
||||||
|
only learn how to use them.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||||
|
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||||
|
too difficult, you need to make more choices than you need in GO and other
|
||||||
|
languages designed with a concurrent programming in mind and ships with a
|
||||||
|
runtime.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||||
|
known to have some issues regarding ergonomics. Turns out that modelling
|
||||||
|
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||||
|
and type signatures with a type system as Rust.</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||||
|
<ol start="4">
|
||||||
|
<li>Async await syntax was recently stabilized</li>
|
||||||
|
</ol>
|
||||||
|
<p>what we'll
|
||||||
|
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
||||||
<p>Before we start implementing our <code>Futures</code> , we'll go through some background
|
<p>Before we start implementing our <code>Futures</code> , we'll go through some background
|
||||||
information that will help demystify some of the concepts we encounter.</p>
|
information that will help demystify some of the concepts we encounter.</p>
|
||||||
@@ -176,8 +243,8 @@ object we construct our selves.</p>
|
|||||||
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
||||||
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
||||||
run the following code:</p>
|
run the following code:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust"><span class="boring">use std::mem::size_of;
|
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
|
||||||
</span>trait SomeTrait { }
|
trait SomeTrait { }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||||
@@ -330,6 +397,8 @@ preferred runtime to actually do the scheduling and I/O queues.</p>
|
|||||||
<p>It's important to know that Rust doesn't provide a runtime, so you have to choose
|
<p>It's important to know that Rust doesn't provide a runtime, so you have to choose
|
||||||
one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a> are two popular ones.</p>
|
one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a> are two popular ones.</p>
|
||||||
<p>With that out of the way, let's move on to our main example.</p>
|
<p>With that out of the way, let's move on to our main example.</p>
|
||||||
|
<h1><a class="header" href="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
|
||||||
|
<h1><a class="header" href="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
|
||||||
<h1><a class="header" href="#naive-example" id="naive-example">Naive example</a></h1>
|
<h1><a class="header" href="#naive-example" id="naive-example">Naive example</a></h1>
|
||||||
<pre><pre class="playpen"><code class="language-rust">use std::sync::atomic::{AtomicUsize, Ordering};
|
<pre><pre class="playpen"><code class="language-rust">use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::mpsc::{channel, Sender};
|
use std::sync::mpsc::{channel, Sender};
|
||||||
@@ -479,118 +548,118 @@ fn main() {
|
|||||||
|
|
||||||
executor_run(reactor, readylist);
|
executor_run(reactor, readylist);
|
||||||
}
|
}
|
||||||
<span class="boring">// ====== EXECUTOR ======
|
# // ====== EXECUTOR ======
|
||||||
</span><span class="boring">fn executor_run(mut reactor: Reactor, rl: Arc<Mutex<Vec<usize>>>) {
|
# fn executor_run(mut reactor: Reactor, rl: Arc<Mutex<Vec<usize>>>) {
|
||||||
</span><span class="boring"> let start = Instant::now();
|
# let start = Instant::now();
|
||||||
</span><span class="boring"> loop {
|
# loop {
|
||||||
</span><span class="boring"> let mut rl_locked = rl.lock().unwrap();
|
# let mut rl_locked = rl.lock().unwrap();
|
||||||
</span><span class="boring"> while let Some(event) = rl_locked.pop() {
|
# while let Some(event) = rl_locked.pop() {
|
||||||
</span><span class="boring"> let dur = (Instant::now() - start).as_secs_f32();
|
# let dur = (Instant::now() - start).as_secs_f32();
|
||||||
</span><span class="boring"> println!("Event {} just happened at time: {:.2}.", event, dur);
|
# println!("Event {} just happened at time: {:.2}.", event, dur);
|
||||||
</span><span class="boring"> reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
|
# reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> drop(rl_locked);
|
# drop(rl_locked);
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> if reactor.outstanding.load(Ordering::Relaxed) == 0 {
|
# if reactor.outstanding.load(Ordering::Relaxed) == 0 {
|
||||||
</span><span class="boring"> reactor.close();
|
# reactor.close();
|
||||||
</span><span class="boring"> break;
|
# break;
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> thread::park();
|
# thread::park();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">// ====== "FUTURE" IMPL ======
|
# // ====== "FUTURE" IMPL ======
|
||||||
</span><span class="boring">#[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
</span><span class="boring">struct MyWaker {
|
# struct MyWaker {
|
||||||
</span><span class="boring"> id: usize,
|
# id: usize,
|
||||||
</span><span class="boring"> thread: thread::Thread,
|
# thread: thread::Thread,
|
||||||
</span><span class="boring"> readylist: Arc<Mutex<Vec<usize>>>,
|
# readylist: Arc<Mutex<Vec<usize>>>,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl MyWaker {
|
# impl MyWaker {
|
||||||
</span><span class="boring"> fn new(id: usize, thread: thread::Thread, readylist: Arc<Mutex<Vec<usize>>>) -> Self {
|
# fn new(id: usize, thread: thread::Thread, readylist: Arc<Mutex<Vec<usize>>>) -> Self {
|
||||||
</span><span class="boring"> MyWaker {
|
# MyWaker {
|
||||||
</span><span class="boring"> id,
|
# id,
|
||||||
</span><span class="boring"> thread,
|
# thread,
|
||||||
</span><span class="boring"> readylist,
|
# readylist,
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn wake(&self) {
|
# fn wake(&self) {
|
||||||
</span><span class="boring"> self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
|
# self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
|
||||||
</span><span class="boring"> self.thread.unpark();
|
# self.thread.unpark();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">#[derive(Debug, Clone)]
|
# #[derive(Debug, Clone)]
|
||||||
</span><span class="boring">pub struct Task {
|
# pub struct Task {
|
||||||
</span><span class="boring"> id: usize,
|
# id: usize,
|
||||||
</span><span class="boring"> pending: bool,
|
# pending: bool,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">// ===== REACTOR =====
|
# // ===== REACTOR =====
|
||||||
</span><span class="boring">struct Reactor {
|
# struct Reactor {
|
||||||
</span><span class="boring"> dispatcher: Sender<Event>,
|
# dispatcher: Sender<Event>,
|
||||||
</span><span class="boring"> handle: Option<JoinHandle<()>>,
|
# handle: Option<JoinHandle<()>>,
|
||||||
</span><span class="boring"> outstanding: AtomicUsize,
|
# outstanding: AtomicUsize,
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">#[derive(Debug)]
|
# #[derive(Debug)]
|
||||||
</span><span class="boring">enum Event {
|
# enum Event {
|
||||||
</span><span class="boring"> Close,
|
# Close,
|
||||||
</span><span class="boring"> Simple(MyWaker, u64),
|
# Simple(MyWaker, u64),
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl Reactor {
|
# impl Reactor {
|
||||||
</span><span class="boring"> fn new() -> Self {
|
# fn new() -> Self {
|
||||||
</span><span class="boring"> let (tx, rx) = channel::<Event>();
|
# let (tx, rx) = channel::<Event>();
|
||||||
</span><span class="boring"> let mut handles = vec![];
|
# let mut handles = vec![];
|
||||||
</span><span class="boring"> let handle = thread::spawn(move || {
|
# let handle = thread::spawn(move || {
|
||||||
</span><span class="boring"> // This simulates some I/O resource
|
# // This simulates some I/O resource
|
||||||
</span><span class="boring"> for event in rx {
|
# for event in rx {
|
||||||
</span><span class="boring"> match event {
|
# match event {
|
||||||
</span><span class="boring"> Event::Close => break,
|
# Event::Close => break,
|
||||||
</span><span class="boring"> Event::Simple(mywaker, duration) => {
|
# Event::Simple(mywaker, duration) => {
|
||||||
</span><span class="boring"> let event_handle = thread::spawn(move || {
|
# let event_handle = thread::spawn(move || {
|
||||||
</span><span class="boring"> thread::sleep(Duration::from_secs(duration));
|
# thread::sleep(Duration::from_secs(duration));
|
||||||
</span><span class="boring"> mywaker.wake();
|
# mywaker.wake();
|
||||||
</span><span class="boring"> });
|
# });
|
||||||
</span><span class="boring"> handles.push(event_handle);
|
# handles.push(event_handle);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> for handle in handles {
|
# for handle in handles {
|
||||||
</span><span class="boring"> handle.join().unwrap();
|
# handle.join().unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> });
|
# });
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> Reactor {
|
# Reactor {
|
||||||
</span><span class="boring"> dispatcher: tx,
|
# dispatcher: tx,
|
||||||
</span><span class="boring"> handle: Some(handle),
|
# handle: Some(handle),
|
||||||
</span><span class="boring"> outstanding: AtomicUsize::new(0),
|
# outstanding: AtomicUsize::new(0),
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn register(&mut self, duration: u64, mywaker: MyWaker) {
|
# fn register(&mut self, duration: u64, mywaker: MyWaker) {
|
||||||
</span><span class="boring"> self.dispatcher
|
# self.dispatcher
|
||||||
</span><span class="boring"> .send(Event::Simple(mywaker, duration))
|
# .send(Event::Simple(mywaker, duration))
|
||||||
</span><span class="boring"> .unwrap();
|
# .unwrap();
|
||||||
</span><span class="boring"> self.outstanding.fetch_add(1, Ordering::Relaxed);
|
# self.outstanding.fetch_add(1, Ordering::Relaxed);
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring"> fn close(&mut self) {
|
# fn close(&mut self) {
|
||||||
</span><span class="boring"> self.dispatcher.send(Event::Close).unwrap();
|
# self.dispatcher.send(Event::Close).unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span><span class="boring">
|
#
|
||||||
</span><span class="boring">impl Drop for Reactor {
|
# impl Drop for Reactor {
|
||||||
</span><span class="boring"> fn drop(&mut self) {
|
# fn drop(&mut self) {
|
||||||
</span><span class="boring"> self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
# self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
||||||
</span><span class="boring"> }
|
# }
|
||||||
</span><span class="boring">}
|
# }
|
||||||
</span></code></pre></pre>
|
</code></pre></pre>
|
||||||
<h1><a class="header" href="#proper-waker" id="proper-waker">Proper Waker</a></h1>
|
<h1><a class="header" href="#proper-waker" id="proper-waker">Proper Waker</a></h1>
|
||||||
<h1><a class="header" href="#proper-future" id="proper-future">Proper Future</a></h1>
|
<h1><a class="header" href="#proper-future" id="proper-future">Proper Future</a></h1>
|
||||||
<h1><a class="header" href="#supporting-asyncawait" id="supporting-asyncawait">Supporting async/await</a></h1>
|
<h1><a class="header" href="#supporting-asyncawait" id="supporting-asyncawait">Supporting async/await</a></h1>
|
||||||
@@ -618,30 +687,7 @@ fn main() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Livereload script (if served using the cli tool) -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
var socket = new WebSocket("ws://localhost:3001");
|
|
||||||
socket.onmessage = function (event) {
|
|
||||||
if (event.data === "reload") {
|
|
||||||
socket.close();
|
|
||||||
location.reload(true); // force reload from server (not from cache)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.playpen_copyable = true;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
196
src/0_1_1_trait_objects.md
Normal file
196
src/0_1_1_trait_objects.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Trait objects and fat pointers
|
||||||
|
|
||||||
|
## Trait objects and dynamic dispatch
|
||||||
|
|
||||||
|
The single most confusing topic we encounter when implementing our own `Futures`
|
||||||
|
is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
|
||||||
|
which allows using dynamic dispatch to call methods on a _type erased_ trait
|
||||||
|
object we construct our selves.
|
||||||
|
|
||||||
|
If you want to know more about dynamic dispatch in Rust I can recommend this article:
|
||||||
|
|
||||||
|
https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/
|
||||||
|
|
||||||
|
Let's explain this a bit more in detail.
|
||||||
|
|
||||||
|
## Fat pointers in Rust
|
||||||
|
|
||||||
|
Let's take a look at the size of some different pointer types in Rust. If we
|
||||||
|
run the following code:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
# use std::mem::size_of;
|
||||||
|
trait SomeTrait { }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||||
|
println!("Size of &i32: {}", size_of::<&i32>());
|
||||||
|
println!("Size of &Box<i32>: {}", size_of::<&Box<i32>>());
|
||||||
|
println!("Size of Box<Trait>: {}", size_of::<Box<SomeTrait>>());
|
||||||
|
println!("Size of &dyn Trait: {}", size_of::<&dyn SomeTrait>());
|
||||||
|
println!("Size of &[i32]: {}", size_of::<&[i32]>());
|
||||||
|
println!("Size of &[&dyn Trait]: {}", size_of::<&[&dyn SomeTrait]>());
|
||||||
|
println!("Size of [i32; 10]: {}", size_of::<[i32; 10]>());
|
||||||
|
println!("Size of [&dyn Trait; 10]: {}", size_of::<[&dyn SomeTrait; 10]>());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As you see from the output after running this, the sizes of the references varies.
|
||||||
|
Most are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||||
|
bytes.
|
||||||
|
|
||||||
|
The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
||||||
|
information.
|
||||||
|
|
||||||
|
**In the case of `&[i32]` :**
|
||||||
|
|
||||||
|
* The first 8 bytes is the actual pointer to the first element in the array
|
||||||
|
|
||||||
|
(or part of an array the slice refers to)
|
||||||
|
|
||||||
|
* The second 8 bytes is the length of the slice.
|
||||||
|
|
||||||
|
The one we'll concern ourselves about is the references to traits, or
|
||||||
|
_trait objects_ as they're called in Rust.
|
||||||
|
|
||||||
|
`&dyn SomeTrait` is an example of a _trait object_
|
||||||
|
|
||||||
|
The layout for a pointer to a _trait object_ looks like this:
|
||||||
|
|
||||||
|
* The first 8 bytes points to the `data` for the trait object
|
||||||
|
* The second 8 bytes points to the `vtable` for the trait object
|
||||||
|
|
||||||
|
The reason for this is to allow us to refer to an object we know nothing about
|
||||||
|
except that it implements the methods defined by our trait. To allow this we use
|
||||||
|
dynamic dispatch.
|
||||||
|
|
||||||
|
Let's explain this in code instead of words by implementing our own trait
|
||||||
|
object from these parts:
|
||||||
|
|
||||||
|
``` rust
|
||||||
|
// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||||
|
trait Test {
|
||||||
|
fn add(&self) -> i32;
|
||||||
|
fn sub(&self) -> i32;
|
||||||
|
fn mul(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will represent our home brewn fat pointer to a trait object
|
||||||
|
#[repr(C)]
|
||||||
|
struct FatPointer<'a> {
|
||||||
|
/// A reference is a pointer to an instantiated `Data` instance
|
||||||
|
data: &'a mut Data,
|
||||||
|
/// Since we need to pass in literal values like length and alignment it's
|
||||||
|
/// easiest for us to convert pointers to usize-integers instead of the other way around.
|
||||||
|
vtable: *const usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the data in our trait object. It's just two numbers we want to operate on.
|
||||||
|
struct Data {
|
||||||
|
a: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== function definitions ======
|
||||||
|
fn add(s: &Data) -> i32 {
|
||||||
|
s.a + s.b
|
||||||
|
}
|
||||||
|
fn sub(s: &Data) -> i32 {
|
||||||
|
s.a - s.b
|
||||||
|
}
|
||||||
|
fn mul(s: &Data) -> i32 {
|
||||||
|
s.a * s.b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut data = Data {a: 3, b: 2};
|
||||||
|
// vtable is like special purpose array of pointer-length types with a fixed
|
||||||
|
// format where the three first values has a special meaning like the
|
||||||
|
// length of the array is encoded in the array itself as the second value.
|
||||||
|
let vtable = vec![
|
||||||
|
0, // pointer to `Drop` (which we're not implementing here)
|
||||||
|
6, // lenght of vtable
|
||||||
|
8, // alignment
|
||||||
|
// we need to make sure we add these in the same order as defined in the Trait.
|
||||||
|
// Try changing the order of add and sub and see what happens.
|
||||||
|
add as usize, // function pointer
|
||||||
|
sub as usize, // function pointer
|
||||||
|
mul as usize, // function pointer
|
||||||
|
];
|
||||||
|
|
||||||
|
let fat_pointer = FatPointer { data: &mut data, vtable: vtable.as_ptr()};
|
||||||
|
let test = unsafe { std::mem::transmute::<FatPointer, &dyn Test>(fat_pointer) };
|
||||||
|
|
||||||
|
// And voalá, it's now a trait object we can call methods on
|
||||||
|
println!("Add: 3 + 2 = {}", test.add());
|
||||||
|
println!("Sub: 3 - 2 = {}", test.sub());
|
||||||
|
println!("Mul: 3 * 2 = {}", test.mul());
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If you run this code by pressing the "play" button at the top you'll se it
|
||||||
|
outputs just what we expect.
|
||||||
|
|
||||||
|
This code example is editable so you can change it
|
||||||
|
and run it to see what happens.
|
||||||
|
|
||||||
|
The reason we go through this will be clear later on when we implement our own
|
||||||
|
`Waker` we'll actually set up a `vtable` like we do here to and knowing what
|
||||||
|
it is will make this much less mysterious.
|
||||||
|
|
||||||
|
## Reactor/Executor pattern
|
||||||
|
|
||||||
|
If you don't know what this is, you should take a few minutes and read about
|
||||||
|
it. You will encounter the term `Reactor` and `Executor` a lot when working
|
||||||
|
with async code in Rust.
|
||||||
|
|
||||||
|
I have written a quick introduction explaining this pattern before which you
|
||||||
|
can take a look at here:
|
||||||
|
|
||||||
|
|
||||||
|
[![homepage][1]][2]
|
||||||
|
|
||||||
|
<div style="text-align:center">
|
||||||
|
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
I'll re-iterate the most important parts here.
|
||||||
|
|
||||||
|
This pattern consists of at least 2 parts:
|
||||||
|
|
||||||
|
1. A reactor
|
||||||
|
- handles some kind of event queue
|
||||||
|
- has the responsibility of respoonding to events
|
||||||
|
2. An executor
|
||||||
|
- Often has a scheduler
|
||||||
|
- Holds a set of suspended tasks, and has the responsibility of resuming
|
||||||
|
them when an event has occurred
|
||||||
|
3. The concept of a task
|
||||||
|
- A set of operations that can be stopped half way and resumed later on
|
||||||
|
|
||||||
|
This is a pattern not only used in Rust, but it's very popular in Rust due to
|
||||||
|
how well it separates concerns between handling and scheduling tasks, and queing
|
||||||
|
and responding to I/O events.
|
||||||
|
|
||||||
|
The only thing Rust as a language defines is the _task_. In Rust we call an
|
||||||
|
incorruptible task a `Future`. Futures has a well defined interface, which means
|
||||||
|
they can be used across the entire ecosystem.
|
||||||
|
|
||||||
|
In addition, Rust provides a way for the Reactor and Executor to communicate
|
||||||
|
through the `Waker`. We'll get to know these in the following chapters.
|
||||||
|
|
||||||
|
Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||||
|
programmers meet when faced with async code, and still not dictate any
|
||||||
|
preferred runtime to actually do the scheduling and I/O queues.
|
||||||
|
|
||||||
|
It's important to know that Rust doesn't provide a runtime, so you have to choose
|
||||||
|
one. [async std](https://github.com/async-rs/async-std) and [tokio](https://github.com/tokio-rs/tokio) are two popular ones.
|
||||||
|
|
||||||
|
With that out of the way, let's move on to our main example.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1]: ./assets/reactorexecutor.png
|
||||||
|
[2]: https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern
|
||||||
28
src/0_1_2_generators_pin.md
Normal file
28
src/0_1_2_generators_pin.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Generators and Pin
|
||||||
|
|
||||||
|
So the second difficult part that there seems to be a lot of questions about
|
||||||
|
is Generators and the `Pin` type.
|
||||||
|
|
||||||
|
## Generators
|
||||||
|
|
||||||
|
```
|
||||||
|
**Relevant for:**
|
||||||
|
|
||||||
|
- Understanding how the async/await syntax works
|
||||||
|
- Why we need `Pin`
|
||||||
|
- Why Rusts async model is extremely efficient
|
||||||
|
```
|
||||||
|
|
||||||
|
The motivation for `Generators` can be found in [RFC#2033][rfc2033]. It's very
|
||||||
|
well written and I can recommend reading through it (it talks as much about
|
||||||
|
async/await as it does about generators).
|
||||||
|
|
||||||
|
Basically, there were three main options that were discussed when Rust was
|
||||||
|
desiging how the language would handle concurrency:
|
||||||
|
|
||||||
|
1. Stackfull coroutines, better known as green threads.
|
||||||
|
2. Using combinators.
|
||||||
|
3. Stackless coroutines, better known as generators.
|
||||||
|
|
||||||
|
|
||||||
|
[rfc2033]: https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md
|
||||||
@@ -15,197 +15,3 @@ try to give a high level overview that will make it easier to learn Rusts
|
|||||||
* [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html)
|
* [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html)
|
||||||
* [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html)
|
* [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html)
|
||||||
|
|
||||||
## Trait objects and dynamic dispatch
|
|
||||||
|
|
||||||
The single most confusing topic we encounter when implementing our own `Futures`
|
|
||||||
is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
|
|
||||||
which allows using dynamic dispatch to call methods on a _type erased_ trait
|
|
||||||
object we construct our selves.
|
|
||||||
|
|
||||||
If you want to know more about dynamic dispatch in Rust I can recommend this article:
|
|
||||||
|
|
||||||
https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/
|
|
||||||
|
|
||||||
Let's explain this a bit more in detail.
|
|
||||||
|
|
||||||
## Fat pointers in Rust
|
|
||||||
|
|
||||||
Let's take a look at the size of some different pointer types in Rust. If we
|
|
||||||
run the following code:
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
# use std::mem::size_of;
|
|
||||||
trait SomeTrait { }
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
|
||||||
println!("Size of &i32: {}", size_of::<&i32>());
|
|
||||||
println!("Size of &Box<i32>: {}", size_of::<&Box<i32>>());
|
|
||||||
println!("Size of Box<Trait>: {}", size_of::<Box<SomeTrait>>());
|
|
||||||
println!("Size of &dyn Trait: {}", size_of::<&dyn SomeTrait>());
|
|
||||||
println!("Size of &[i32]: {}", size_of::<&[i32]>());
|
|
||||||
println!("Size of &[&dyn Trait]: {}", size_of::<&[&dyn SomeTrait]>());
|
|
||||||
println!("Size of [i32; 10]: {}", size_of::<[i32; 10]>());
|
|
||||||
println!("Size of [&dyn Trait; 10]: {}", size_of::<[&dyn SomeTrait; 10]>());
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
As you see from the output after running this, the sizes of the references varies.
|
|
||||||
Most are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
|
||||||
bytes.
|
|
||||||
|
|
||||||
The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
|
||||||
information.
|
|
||||||
|
|
||||||
**In the case of `&[i32]` :**
|
|
||||||
|
|
||||||
* The first 8 bytes is the actual pointer to the first element in the array
|
|
||||||
|
|
||||||
(or part of an array the slice refers to)
|
|
||||||
|
|
||||||
* The second 8 bytes is the length of the slice.
|
|
||||||
|
|
||||||
The one we'll concern ourselves about is the references to traits, or
|
|
||||||
_trait objects_ as they're called in Rust.
|
|
||||||
|
|
||||||
`&dyn SomeTrait` is an example of a _trait object_
|
|
||||||
|
|
||||||
The layout for a pointer to a _trait object_ looks like this:
|
|
||||||
|
|
||||||
* The first 8 bytes points to the `data` for the trait object
|
|
||||||
* The second 8 bytes points to the `vtable` for the trait object
|
|
||||||
|
|
||||||
The reason for this is to allow us to refer to an object we know nothing about
|
|
||||||
except that it implements the methods defined by our trait. To allow this we use
|
|
||||||
dynamic dispatch.
|
|
||||||
|
|
||||||
Let's explain this in code instead of words by implementing our own trait
|
|
||||||
object from these parts:
|
|
||||||
|
|
||||||
``` rust
|
|
||||||
// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
|
||||||
trait Test {
|
|
||||||
fn add(&self) -> i32;
|
|
||||||
fn sub(&self) -> i32;
|
|
||||||
fn mul(&self) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will represent our home brewn fat pointer to a trait object
|
|
||||||
#[repr(C)]
|
|
||||||
struct FatPointer<'a> {
|
|
||||||
/// A reference is a pointer to an instantiated `Data` instance
|
|
||||||
data: &'a mut Data,
|
|
||||||
/// Since we need to pass in literal values like length and alignment it's
|
|
||||||
/// easiest for us to convert pointers to usize-integers instead of the other way around.
|
|
||||||
vtable: *const usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the data in our trait object. It's just two numbers we want to operate on.
|
|
||||||
struct Data {
|
|
||||||
a: i32,
|
|
||||||
b: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====== function definitions ======
|
|
||||||
fn add(s: &Data) -> i32 {
|
|
||||||
s.a + s.b
|
|
||||||
}
|
|
||||||
fn sub(s: &Data) -> i32 {
|
|
||||||
s.a - s.b
|
|
||||||
}
|
|
||||||
fn mul(s: &Data) -> i32 {
|
|
||||||
s.a * s.b
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut data = Data {a: 3, b: 2};
|
|
||||||
// vtable is like special purpose array of pointer-length types with a fixed
|
|
||||||
// format where the three first values has a special meaning like the
|
|
||||||
// length of the array is encoded in the array itself as the second value.
|
|
||||||
let vtable = vec![
|
|
||||||
0, // pointer to `Drop` (which we're not implementing here)
|
|
||||||
6, // lenght of vtable
|
|
||||||
8, // alignment
|
|
||||||
// we need to make sure we add these in the same order as defined in the Trait.
|
|
||||||
// Try changing the order of add and sub and see what happens.
|
|
||||||
add as usize, // function pointer
|
|
||||||
sub as usize, // function pointer
|
|
||||||
mul as usize, // function pointer
|
|
||||||
];
|
|
||||||
|
|
||||||
let fat_pointer = FatPointer { data: &mut data, vtable: vtable.as_ptr()};
|
|
||||||
let test = unsafe { std::mem::transmute::<FatPointer, &dyn Test>(fat_pointer) };
|
|
||||||
|
|
||||||
// And voalá, it's now a trait object we can call methods on
|
|
||||||
println!("Add: 3 + 2 = {}", test.add());
|
|
||||||
println!("Sub: 3 - 2 = {}", test.sub());
|
|
||||||
println!("Mul: 3 * 2 = {}", test.mul());
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
If you run this code by pressing the "play" button at the top you'll se it
|
|
||||||
outputs just what we expect.
|
|
||||||
|
|
||||||
This code example is editable so you can change it
|
|
||||||
and run it to see what happens.
|
|
||||||
|
|
||||||
The reason we go through this will be clear later on when we implement our own
|
|
||||||
`Waker` we'll actually set up a `vtable` like we do here to and knowing what
|
|
||||||
it is will make this much less mysterious.
|
|
||||||
|
|
||||||
## Reactor/Executor pattern
|
|
||||||
|
|
||||||
If you don't know what this is, you should take a few minutes and read about
|
|
||||||
it. You will encounter the term `Reactor` and `Executor` a lot when working
|
|
||||||
with async code in Rust.
|
|
||||||
|
|
||||||
I have written a quick introduction explaining this pattern before which you
|
|
||||||
can take a look at here:
|
|
||||||
|
|
||||||
|
|
||||||
[![homepage][1]][2]
|
|
||||||
|
|
||||||
<div style="text-align:center">
|
|
||||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
I'll re-iterate the most important parts here.
|
|
||||||
|
|
||||||
This pattern consists of at least 2 parts:
|
|
||||||
|
|
||||||
1. A reactor
|
|
||||||
- handles some kind of event queue
|
|
||||||
- has the responsibility of respoonding to events
|
|
||||||
2. An executor
|
|
||||||
- Often has a scheduler
|
|
||||||
- Holds a set of suspended tasks, and has the responsibility of resuming
|
|
||||||
them when an event has occurred
|
|
||||||
3. The concept of a task
|
|
||||||
- A set of operations that can be stopped half way and resumed later on
|
|
||||||
|
|
||||||
This is a pattern not only used in Rust, but it's very popular in Rust due to
|
|
||||||
how well it separates concerns between handling and scheduling tasks, and queing
|
|
||||||
and responding to I/O events.
|
|
||||||
|
|
||||||
The only thing Rust as a language defines is the _task_. In Rust we call an
|
|
||||||
incorruptible task a `Future`. Futures has a well defined interface, which means
|
|
||||||
they can be used across the entire ecosystem.
|
|
||||||
|
|
||||||
In addition, Rust provides a way for the Reactor and Executor to communicate
|
|
||||||
through the `Waker`. We'll get to know these in the following chapters.
|
|
||||||
|
|
||||||
Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
|
||||||
programmers meet when faced with async code, and still not dictate any
|
|
||||||
preferred runtime to actually do the scheduling and I/O queues.
|
|
||||||
|
|
||||||
It's important to know that Rust doesn't provide a runtime, so you have to choose
|
|
||||||
one. [async std](https://github.com/async-rs/async-std) and [tokio](https://github.com/tokio-rs/tokio) are two popular ones.
|
|
||||||
|
|
||||||
With that out of the way, let's move on to our main example.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: ./assets/reactorexecutor.png
|
|
||||||
[2]: https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
- [Introduction](./0_0_introduction.md)
|
- [Introduction](./0_0_introduction.md)
|
||||||
- [Some background information](./0_1_background_information.md)
|
- [Some background information](./0_1_background_information.md)
|
||||||
|
- [Trait objects and fat pointers](./0_1_1_trait_objects.md)
|
||||||
|
- [Generators and Pin](./0_1_2_generators_pin.md)
|
||||||
- [Naive example](./0_2_naive_implementation.md)
|
- [Naive example](./0_2_naive_implementation.md)
|
||||||
- [Proper Waker](./0_3_proper_waker.md)
|
- [Proper Waker](./0_3_proper_waker.md)
|
||||||
- [Proper Future](0_4_proper_future.md)
|
- [Proper Future](0_4_proper_future.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user