finished all but the main example
This commit is contained in:
@@ -78,7 +78,7 @@
|
||||
|
||||
<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="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html" class="active"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: 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="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html" class="active"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -146,62 +146,73 @@
|
||||
<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>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>Understanding how the Waker object is constructed</li>
|
||||
<li>Getting a basic feel for "type erased" objects and what they are</li>
|
||||
<li>Learning the basics of dynamic dispatch</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h2><a class="header" href="#trait-objects-and-dynamic-dispatch" id="trait-objects-and-dynamic-dispatch">Trait objects and dynamic dispatch</a></h2>
|
||||
<p>The single most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
<p>One of the most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
is how we implement a <code>Waker</code> . Creating a <code>Waker</code> involves creating a <code>vtable</code>
|
||||
which allows using dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
which allows us to use dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
object we construct our selves.</p>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend this article:</p>
|
||||
<p>https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/</p>
|
||||
<blockquote>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend an article written by Adam Schwalm called <a href="https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/">Exploring Dynamic Dispatch in Rust</a>.</p>
|
||||
</blockquote>
|
||||
<p>Let's explain this a bit more in detail.</p>
|
||||
<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
|
||||
run the following code:</p>
|
||||
run the following code. <em>(You'll have to press "play" to see the output)</em>:</p>
|
||||
<pre><pre class="playpen"><code class="language-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]>());
|
||||
println!("======== The size of different pointers in Rust: ========");
|
||||
println!("&dyn Trait:-----{}", size_of::<&dyn SomeTrait>());
|
||||
println!("&[&dyn Trait]:--{}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Box<Trait>:-----{}", size_of::<Box<SomeTrait>>());
|
||||
println!("&i32:-----------{}", size_of::<&i32>());
|
||||
println!("&[i32]:---------{}", size_of::<&[i32]>());
|
||||
println!("Box<i32>:-------{}", size_of::<Box<i32>>());
|
||||
println!("&Box<i32>:------{}", size_of::<&Box<i32>>());
|
||||
println!("[&dyn Trait;4]:-{}", size_of::<[&dyn SomeTrait; 4]>());
|
||||
println!("[i32;4]:--------{}", size_of::<[i32; 4]>());
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>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
|
||||
Many are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
bytes.</p>
|
||||
<p>The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
||||
information.</p>
|
||||
<p><strong>In the case of <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array</li>
|
||||
</ul>
|
||||
<p>(or part of an array the slice refers to)</p>
|
||||
<p><strong>Example <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array (or part of an array the slice refers to)</li>
|
||||
<li>The second 8 bytes is the length of the slice.</li>
|
||||
</ul>
|
||||
<p>The one we'll concern ourselves about is the references to traits, or
|
||||
<em>trait objects</em> as they're called in Rust.</p>
|
||||
<p><code>&dyn SomeTrait</code> is an example of a <em>trait object</em> </p>
|
||||
<p><strong>Example <code>&dyn SomeTrait</code>:</strong></p>
|
||||
<p>This is the type of fat pointer we'll concern ourselves about going forward.
|
||||
<code>&dyn SomeTrait</code> is a reference to a trait, or what Rust calls <em>trait objects</em>.</p>
|
||||
<p>The layout for a pointer to a <em>trait object</em> looks like this: </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes points to the <code>data</code> for the trait object</li>
|
||||
<li>The second 8 bytes points to the <code>vtable</code> for the trait object</li>
|
||||
</ul>
|
||||
<p>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.</p>
|
||||
except that it implements the methods defined by our trait. To allow accomplish this we use <em>dynamic dispatch</em>.</p>
|
||||
<p>Let's explain this in code instead of words by implementing our own trait
|
||||
object from these parts:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
<blockquote>
|
||||
<p>This is an example of <em>editable</em> code. You can change everything in the example
|
||||
and try to run it. If you want to go back, press the undo symbol. Keep an eye
|
||||
out for these as we go forward. Many examples will be editable.</p>
|
||||
</blockquote>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">// 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 add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn mul(&self) -> i32;
|
||||
}
|
||||
|
||||
@@ -241,10 +252,10 @@ fn main() {
|
||||
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
|
||||
add as usize, // function pointer - try changing the order of `add`
|
||||
sub as usize, // function pointer - and `sub` to see what happens
|
||||
mul as usize, // function pointer
|
||||
];
|
||||
|
||||
@@ -258,59 +269,9 @@ fn main() {
|
||||
}
|
||||
|
||||
</code></pre></pre>
|
||||
<p>If you run this code by pressing the "play" button at the top you'll se it
|
||||
outputs just what we expect.</p>
|
||||
<p>This code example is editable so you can change it
|
||||
and run it to see what happens.</p>
|
||||
<p>The reason we go through this will be clear later on when we implement our own
|
||||
<code>Waker</code> we'll actually set up a <code>vtable</code> like we do here to and knowing what
|
||||
it is will make this much less mysterious.</p>
|
||||
<h2><a class="header" href="#reactorexecutor-pattern" id="reactorexecutor-pattern">Reactor/Executor pattern</a></h2>
|
||||
<p>If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term <code>Reactor</code> and <code>Executor</code> a lot when working
|
||||
with async code in Rust.</p>
|
||||
<p>I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:</p>
|
||||
<p><a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern"><img src="./assets/reactorexecutor.png" alt="homepage" /></a></p>
|
||||
<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>
|
||||
<p>I'll re-iterate the most important parts here.</p>
|
||||
<p>This pattern consists of at least 2 parts:</p>
|
||||
<ol>
|
||||
<li>A reactor
|
||||
<ul>
|
||||
<li>handles some kind of event queue</li>
|
||||
<li>has the responsibility of respoonding to events</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>An executor
|
||||
<ul>
|
||||
<li>Often has a scheduler</li>
|
||||
<li>Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>The concept of a task
|
||||
<ul>
|
||||
<li>A set of operations that can be stopped half way and resumed later on</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>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.</p>
|
||||
<p>The only thing Rust as a language defines is the <em>task</em>. In Rust we call an
|
||||
incorruptible task a <code>Future</code>. Futures has a well defined interface, which means
|
||||
they can be used across the entire ecosystem.</p>
|
||||
<p>In addition, Rust provides a way for the Reactor and Executor to communicate
|
||||
through the <code>Waker</code>. We'll get to know these in the following chapters.</p>
|
||||
<p>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.</p>
|
||||
<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>
|
||||
<p>With that out of the way, let's move on to our main example.</p>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user