finished book!!!!!!
This commit is contained in:
@@ -414,9 +414,10 @@ impl Test {
|
||||
_marker: PhantomPinned,
|
||||
}
|
||||
}
|
||||
fn init(&mut self) {
|
||||
fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
let self_ptr: *const String = &self.a;
|
||||
self.b = self_ptr;
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
this.b = self_ptr;
|
||||
}
|
||||
|
||||
fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
@@ -435,15 +436,18 @@ and let users avoid <code>unsafe</code> we need to pin our data on the heap inst
|
||||
we'll show in a second.</p>
|
||||
<p>Let's see what happens if we run our example now:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">pub fn main() {
|
||||
// test1 is safe to move before we initialize it
|
||||
let mut test1 = Test::new("test1");
|
||||
test1.init();
|
||||
let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
// Notice how we shadow `test1` to prevent it from beeing accessed again
|
||||
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1.as_mut());
|
||||
|
||||
let mut test2 = Test::new("test2");
|
||||
test2.init();
|
||||
let mut test2_pin = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
Test::init(test2.as_mut());
|
||||
|
||||
println!("a: {}, b: {}", Test::a(test1_pin.as_ref()), Test::b(test1_pin.as_ref()));
|
||||
println!("a: {}, b: {}", Test::a(test2_pin.as_ref()), Test::b(test2_pin.as_ref()));
|
||||
println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
|
||||
println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
@@ -466,9 +470,10 @@ we'll show in a second.</p>
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
# }
|
||||
# fn init(&mut self) {
|
||||
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
# let self_ptr: *const String = &self.a;
|
||||
# self.b = self_ptr;
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
@@ -481,18 +486,19 @@ we'll show in a second.</p>
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<p>Now, if we try to pull the same trick which got us in to trouble the last time
|
||||
you'll get a compilation error. So t</p>
|
||||
you'll get a compilation error.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust compile_fail">pub fn main() {
|
||||
let mut test1 = Test::new("test1");
|
||||
test1.init();
|
||||
let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1.as_mut());
|
||||
|
||||
let mut test2 = Test::new("test2");
|
||||
test2.init();
|
||||
let mut test2_pin = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
Test::init(test2.as_mut());
|
||||
|
||||
println!("a: {}, b: {}", Test::a(test1_pin.as_ref()), Test::b(test1_pin.as_ref()));
|
||||
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()));
|
||||
println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
|
||||
std::mem::swap(test1.as_mut(), test2.as_mut());
|
||||
println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
@@ -515,9 +521,10 @@ you'll get a compilation error. So t</p>
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
# }
|
||||
# fn init(&mut self) {
|
||||
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||
# let self_ptr: *const String = &self.a;
|
||||
# self.b = self_ptr;
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
@@ -529,10 +536,22 @@ you'll get a compilation error. So t</p>
|
||||
# }
|
||||
# }
|
||||
</code></pre></pre>
|
||||
<p>As you see from the error you get by running the code the type system prevents
|
||||
us from swapping the pinned pointers.</p>
|
||||
<blockquote>
|
||||
<p>It's important to note that stack pinning will always depend on the current
|
||||
stack frame we're in, so we can't create a self referential object in one
|
||||
stack frame and return it since any pointers we take to "self" is invalidated.</p>
|
||||
<p>It also puts a lot of responsibility in your hands if you pin a value to the
|
||||
stack. A mistake that is easy to make is, forgetting to shadow the original variable
|
||||
since you could drop the pinned pointer and access the old value
|
||||
after it's initialized like this:</p>
|
||||
<pre><code class="language-rust ignore"> let mut test1 = Test::new("test1");
|
||||
let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1_pin.as_mut());
|
||||
drop(test1_pin);
|
||||
println!("{:?}", test1.b);
|
||||
</code></pre>
|
||||
</blockquote>
|
||||
<h2><a class="header" href="#pinning-to-the-heap" id="pinning-to-the-heap">Pinning to the heap</a></h2>
|
||||
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
||||
@@ -580,7 +599,7 @@ pub fn main() {
|
||||
println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>The fact that boxing (heap allocating) a value that implements <code>!Unpin</code> is safe
|
||||
<p>The fact that pinning a heap allocated 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 is no need for us as users of the API to take special care and ensure
|
||||
that the self-referential pointer stays valid.</p>
|
||||
@@ -594,15 +613,15 @@ equivalent to <code>&'a mut T</code>. in other words: <code>Unpin</code> mea
|
||||
to be moved even when pinned, so <code>Pin</code> will have no effect on such a type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: !Unpin</code>. In
|
||||
<p>Getting a <code>&mut T</code> to a pinned T requires unsafe if <code>T: !Unpin</code>. In
|
||||
other words: requiring a pinned pointer to a type which is <code>!Unpin</code> prevents
|
||||
the <em>user</em> of that API from moving that value unless it choses to write <code>unsafe</code>
|
||||
code.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning does nothing special with memory allocation like putting it into some
|
||||
"read only" memory or anything fancy. It only tells the compiler that some
|
||||
operations on this value should be forbidden.</p>
|
||||
"read only" memory or anything fancy. It only uses the type system to prevent
|
||||
certain operations on this value.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Most standard library types implement <code>Unpin</code>. The same goes for most
|
||||
@@ -616,8 +635,9 @@ cases in the API which are being explored.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The implementation behind objects that are <code>!Unpin</code> is most likely unsafe.
|
||||
Moving such a type can cause the universe to crash. As of the time of writing
|
||||
this book, creating and reading fields of a self referential struct still requires <code>unsafe</code>.</p>
|
||||
Moving such a type after it has been pinned can cause the universe to crash. As of the time of writing
|
||||
this book, creating and reading fields of a self referential struct still requires <code>unsafe</code>
|
||||
(the only way to do it is to create a struct containing raw pointers to itself).</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>You can add a <code>!Unpin</code> bound on a type on nightly with a feature flag, or
|
||||
|
||||
Reference in New Issue
Block a user