added main example
This commit is contained in:
@@ -610,227 +610,6 @@ they did their unsafe implementation.</li>
|
||||
</ol>
|
||||
<p>Now, the code which is created and the need for <code>Pin</code> to allow for borrowing
|
||||
across <code>yield</code> points should be pretty clear. </p>
|
||||
<h2><a class="header" href="#pin" id="pin">Pin</a></h2>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for</strong></p>
|
||||
<ol>
|
||||
<li>To understand <code>Generators</code> and <code>Futures</code></li>
|
||||
<li>Knowing how to use <code>Pin</code> is required when implementing your own <code>Future</code></li>
|
||||
<li>To understand self-referential types in Rust</li>
|
||||
<li>This is the way borrowing across <code>await</code> points is accomplished</li>
|
||||
</ol>
|
||||
<p><code>Pin</code> was suggested in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">RFC#2349</a></p>
|
||||
</blockquote>
|
||||
<p>Ping consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Let's start off with some general rules:</p>
|
||||
<ol>
|
||||
<li>Pin does nothing special, it only prevents the user of an API to violate some assumtions you make when writing your (most likely) unsafe code.</li>
|
||||
<li>Most standard library types implement <code>Unpin</code></li>
|
||||
<li><code>Unpin</code> means it's OK for this type to be moved even when pinned.</li>
|
||||
<li>If you <code>Box</code> a value, that boxed value automatcally implements <code>Unpin</code>.</li>
|
||||
<li>The main use case for <code>Pin</code> is to allow self referential types</li>
|
||||
<li>The implementation behind objects that doens't implement <code>Unpin</code> is most likely unsafe
|
||||
<ol>
|
||||
<li><code>Pin</code> prevents users from your code to break the assumtions you make when writing the <code>unsafe</code> implementation</li>
|
||||
<li>It doesn't solve the fact that you'll have to write unsafe code to actually implement it</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>You're not really meant to be implementing <code>!Unpin</code>, but you can on nightly with a feature flag</li>
|
||||
</ol>
|
||||
<blockquote>
|
||||
<p>Unsafe code does not mean it's litterally "unsafe", it only relieves the
|
||||
guarantees you normally get from the compiler. An <code>unsafe</code> implementation can
|
||||
be perfectly safe to do, but you have no safety net.</p>
|
||||
</blockquote>
|
||||
<p>Let's take a look at an example:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">use std::pin::Pin;
|
||||
|
||||
fn main() {
|
||||
let mut test1 = Test::new("test1");
|
||||
test1.init();
|
||||
let mut test2 = Test::new("test2");
|
||||
test2.init();
|
||||
|
||||
println!("a: {}, b: {}", test1.a(), test1.b());
|
||||
std::mem::swap(&mut test1, &mut test2); // try commenting out this line
|
||||
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Test {
|
||||
a: String,
|
||||
b: *const String,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
fn new(txt: &str) -> Self {
|
||||
let a = String::from(txt);
|
||||
Test {
|
||||
a,
|
||||
b: std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
let self_ref: *const String = &self.a;
|
||||
self.b = self_ref;
|
||||
}
|
||||
|
||||
fn a(&self) -> &str {
|
||||
&self.a
|
||||
}
|
||||
|
||||
fn b(&self) -> &String {
|
||||
unsafe {&*(self.b)}
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>As you can see this results in unwanted behavior. The pointer to <code>b</code> stays the
|
||||
same and points to the old value. It's easy to get this to segfault, and fail
|
||||
in other spectacular ways as well.</p>
|
||||
<p>Pin essentially prevents the <strong>user</strong> of your unsafe code
|
||||
(even if that means yourself) move the value after it's pinned.</p>
|
||||
<p>If we change the example to using <code>Pin</code> instead:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
|
||||
pub fn main() {
|
||||
let mut test1 = Test::new("test1");
|
||||
test1.init();
|
||||
let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
let mut test2 = Test::new("test2");
|
||||
test2.init();
|
||||
let mut test2_pin = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
|
||||
println!(
|
||||
"a: {}, b: {}",
|
||||
Test::a(test1_pin.as_ref()),
|
||||
Test::b(test1_pin.as_ref())
|
||||
);
|
||||
|
||||
// Try to uncomment this and see what happens
|
||||
// std::mem::swap(test1_pin.as_mut(), test2_pin.as_mut());
|
||||
println!(
|
||||
"a: {}, b: {}",
|
||||
Test::a(test2_pin.as_ref()),
|
||||
Test::b(test2_pin.as_ref())
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Test {
|
||||
a: String,
|
||||
b: *const String,
|
||||
_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
|
||||
impl Test {
|
||||
fn new(txt: &str) -> Self {
|
||||
let a = String::from(txt);
|
||||
Test {
|
||||
a,
|
||||
b: std::ptr::null(),
|
||||
// This makes our type `!Unpin`
|
||||
_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
fn init(&mut self) {
|
||||
let self_ptr: *const String = &self.a;
|
||||
self.b = self_ptr;
|
||||
}
|
||||
|
||||
fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
&self.get_ref().a
|
||||
}
|
||||
|
||||
fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
unsafe { &*(self.b) }
|
||||
}
|
||||
}
|
||||
|
||||
</code></pre></pre>
|
||||
<p>Now, what we've done here is pinning a stack address. That will always be
|
||||
<code>unsafe</code> if our type implements <code>!Unpin</code>, in other words. That our type is not
|
||||
<code>Unpin</code> which is the norm.</p>
|
||||
<p>We use some tricks here, including requiring an <code>init</code>. If we want to fix that
|
||||
and let users avoid <code>unsafe</code> we need to place our data on the heap.</p>
|
||||
<p>Stack pinning will always depend on the current stack frame we're in, so we
|
||||
can't create a self referential object in one stack frame and return it since
|
||||
any pointers we take to "self" is invalidated.</p>
|
||||
<p>The next example solves some of our friction at the cost of a heap allocation.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editbable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
|
||||
pub fn main() {
|
||||
let mut test1 = Test::new("test1");
|
||||
let mut test2 = Test::new("test2");
|
||||
|
||||
println!("a: {}, b: {}",test1.as_ref().a(), test1.as_ref().b());
|
||||
|
||||
// Try to uncomment this and see what happens
|
||||
// std::mem::swap(&mut test1, &mut test2);
|
||||
println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Test {
|
||||
a: String,
|
||||
b: *const String,
|
||||
_marker: PhantomPinned,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
fn new(txt: &str) -> Pin<Box<Self>> {
|
||||
let a = String::from(txt);
|
||||
let t = Test {
|
||||
a,
|
||||
b: std::ptr::null(),
|
||||
_marker: PhantomPinned,
|
||||
};
|
||||
let mut boxed = Box::pin(t);
|
||||
let self_ptr: *const String = &boxed.as_ref().a;
|
||||
unsafe { boxed.as_mut().get_unchecked_mut().b = self_ptr };
|
||||
|
||||
boxed
|
||||
}
|
||||
|
||||
fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
&self.get_ref().a
|
||||
}
|
||||
|
||||
fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
unsafe { &*(self.b) }
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>Seeing this we're ready to sum up with a few more points to remember about
|
||||
pinning:</p>
|
||||
<ol>
|
||||
<li>Pinning only makes sense to do for types that are <code>!Unpin</code></li>
|
||||
<li>Pinning a <code>!Unpin</code> pointer to the stack will requires <code>unsafe</code></li>
|
||||
<li>Pinning a boxed value will not require <code>unsafe</code>, even if the type is <code>!Unpin</code></li>
|
||||
<li>If T: Unpin (which is the default), then Pin<'a, T> is entirely equivalent to &'a mut T.</li>
|
||||
<li>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: !Unpin</code></li>
|
||||
<li>Pinning is really only useful when implementing self-referential types.<br />
|
||||
For all intents and purposes you can think of <code>!Unpin</code> = self-referential-type</li>
|
||||
</ol>
|
||||
<p>The fact that boxing (heap allocating) a value that implements <code>!Unpin</code> is safe
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address. </p>
|
||||
<p>There are ways to safely give some guarantees on stack pinning as well, but right
|
||||
now you need to use a crate like <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a>:<a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a> to do that.</p>
|
||||
<h3><a class="header" href="#projectionstructural-pinning" id="projectionstructural-pinning">Projection/structural pinning</a></h3>
|
||||
<p>In short, projection is using a field on your type. <code>mystruct.field1</code> is a
|
||||
projection. Structural pinning is using <code>Pin</code> on struct fields. This has several
|
||||
caveats and is not something you'll normally see so I refer to the documentation
|
||||
for that.</p>
|
||||
<h3><a class="header" href="#pin-and-drop" id="pin-and-drop">Pin and Drop</a></h3>
|
||||
<p>The <code>Pin</code> guarantee exists from the moment the value is pinned until it's dropped.
|
||||
In the <code>Drop</code> implementation you take a mutabl reference to <code>self</code>, which means
|
||||
extra care must be taken when implementing <code>Drop</code> for pinned types.</p>
|
||||
<h2><a class="header" href="#putting-it-all-together" id="putting-it-all-together">Putting it all together</a></h2>
|
||||
<p>This is exactly what we'll do when we implement our own <code>Futures</code> stay tuned,
|
||||
we're soon finished.</p>
|
||||
|
||||
</main>
|
||||
|
||||
@@ -870,6 +649,21 @@ we're soon finished.</p>
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user