more precise wording and fixed heap-pinning example which didn't work as expected
This commit is contained in:
@@ -588,7 +588,7 @@ after it's initialized like this:</p>
|
|||||||
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
||||||
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
||||||
doesn't need to implement any unsafe code:</p>
|
doesn't need to implement any unsafe code:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">use std::pin::Pin;
|
<pre><pre class="playpen"><code class="language-rust edition2018">use std::pin::Pin;
|
||||||
use std::marker::PhantomPinned;
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -2068,7 +2068,7 @@ after it's initialized like this:</p>
|
|||||||
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
<p>For completeness let's remove some unsafe and the need for an <code>init</code> method
|
||||||
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
||||||
doesn't need to implement any unsafe code:</p>
|
doesn't need to implement any unsafe code:</p>
|
||||||
<pre><pre class="playpen"><code class="language-rust">use std::pin::Pin;
|
<pre><pre class="playpen"><code class="language-rust edition2018">use std::pin::Pin;
|
||||||
use std::marker::PhantomPinned;
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
36
src/4_pin.md
36
src/4_pin.md
@@ -43,7 +43,7 @@ If you want to you can read a bit of the discussion from the
|
|||||||
## Pinning and self-referential structs
|
## Pinning and self-referential structs
|
||||||
|
|
||||||
Let's start where we left off in the last chapter by making the problem we
|
Let's start where we left off in the last chapter by making the problem we
|
||||||
saw using a self-referential struct in our generator a lot simpler by making
|
saw using a self-references in our generator a lot simpler by making
|
||||||
some self-referential structs that are easier to reason about than our
|
some self-referential structs that are easier to reason about than our
|
||||||
state machines:
|
state machines:
|
||||||
|
|
||||||
@@ -145,9 +145,8 @@ a: test1, b: test1
|
|||||||
a: test2, b: test2
|
a: test2, b: test2
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's see what happens if we swap the data stored at the memory location
|
Let's see what happens if we swap the data stored at the memory location `test1` with the
|
||||||
which `test1` is pointing to with the data stored at the memory location
|
data stored at the memory location `test2` and vice a versa.
|
||||||
`test2` is pointing to and vice a versa.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -311,7 +310,7 @@ impl Test {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, what we've done here is pinning a stack address. That will always be
|
Now, what we've done here is pinning an object to the stack. That will always be
|
||||||
`unsafe` if our type implements `!Unpin`.
|
`unsafe` if our type implements `!Unpin`.
|
||||||
|
|
||||||
We use the same tricks here, including requiring an `init`. If we want to fix that
|
We use the same tricks here, including requiring an `init`. If we want to fix that
|
||||||
@@ -431,10 +430,10 @@ us from swapping the pinned pointers.
|
|||||||
> stack frame we're in, so we can't create a self referential object in one
|
> 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.
|
> stack frame and return it since any pointers we take to "self" is invalidated.
|
||||||
>
|
>
|
||||||
> It also puts a lot of responsibility in your hands if you pin a value to the
|
> It also puts a lot of responsibility in your hands if you pin an object to the
|
||||||
> stack. A mistake that is easy to make is, forgetting to shadow the original variable
|
> 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
|
> since you could drop the `Pin` and access the old value after it's initialized
|
||||||
> after it's initialized like this:
|
> like this:
|
||||||
>
|
>
|
||||||
> ```rust
|
> ```rust
|
||||||
> fn main() {
|
> fn main() {
|
||||||
@@ -489,7 +488,7 @@ For completeness let's remove some unsafe and the need for an `init` method
|
|||||||
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
at the cost of a heap allocation. Pinning to the heap is safe so the user
|
||||||
doesn't need to implement any unsafe code:
|
doesn't need to implement any unsafe code:
|
||||||
|
|
||||||
```rust
|
```rust, edition2018
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::marker::PhantomPinned;
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
@@ -532,7 +531,7 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The fact that it's safe to pin a heap allocated value even if it is `!Unpin`
|
The fact that it's safe to pin heap allocated data even if it is `!Unpin`
|
||||||
makes sense. Once the data is allocated on the heap it will have a stable address.
|
makes sense. Once the data is allocated on the heap it will have a stable address.
|
||||||
|
|
||||||
There is no need for us as users of the API to take special care and ensure
|
There is no need for us as users of the API to take special care and ensure
|
||||||
@@ -561,8 +560,7 @@ certain operations on this value.
|
|||||||
exceptions.
|
exceptions.
|
||||||
|
|
||||||
5. The main use case for `Pin` is to allow self referential types, the whole
|
5. The main use case for `Pin` is to allow self referential types, the whole
|
||||||
justification for stabilizing them was to allow that. There are still corner
|
justification for stabilizing them was to allow that.
|
||||||
cases in the API which are being explored.
|
|
||||||
|
|
||||||
6. The implementation behind objects that are `!Unpin` is most likely unsafe.
|
6. The implementation behind objects that are `!Unpin` is most likely unsafe.
|
||||||
Moving such a type after it has been pinned can cause the universe to crash. As of the time of writing
|
Moving such a type after it has been pinned can cause the universe to crash. As of the time of writing
|
||||||
@@ -572,11 +570,11 @@ this book, creating and reading fields of a self referential struct still requir
|
|||||||
7. You can add a `!Unpin` bound on a type on nightly with a feature flag, or
|
7. You can add a `!Unpin` bound on a type on nightly with a feature flag, or
|
||||||
by adding `std::marker::PhantomPinned` to your type on stable.
|
by adding `std::marker::PhantomPinned` to your type on stable.
|
||||||
|
|
||||||
8. You can either pin a value to memory on the stack or on the heap.
|
8. You can either pin an object to the stack or to the heap.
|
||||||
|
|
||||||
9. Pinning a `!Unpin` pointer to the stack requires `unsafe`
|
9. Pinning a `!Unpin` object to the stack requires `unsafe`
|
||||||
|
|
||||||
10. Pinning a `!Unpin` pointer to the heap does not require `unsafe`. There is a shortcut for doing this using `Box::pin`.
|
10. Pinning a `!Unpin` object to the heap does not require `unsafe`. There is a shortcut for doing this using `Box::pin`.
|
||||||
|
|
||||||
> Unsafe code does not mean it's literally "unsafe", it only relieves the
|
> Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||||
> guarantees you normally get from the compiler. An `unsafe` implementation can
|
> guarantees you normally get from the compiler. An `unsafe` implementation can
|
||||||
@@ -617,11 +615,11 @@ use std::pin::Pin;
|
|||||||
pub fn main() {
|
pub fn main() {
|
||||||
let gen1 = GeneratorA::start();
|
let gen1 = GeneratorA::start();
|
||||||
let gen2 = GeneratorA::start();
|
let gen2 = GeneratorA::start();
|
||||||
// Before we pin the pointers, this is safe to do
|
// Before we pin the data, this is safe to do
|
||||||
// std::mem::swap(&mut gen, &mut gen2);
|
// std::mem::swap(&mut gen, &mut gen2);
|
||||||
|
|
||||||
// constructing a `Pin::new()` on a type which does not implement `Unpin` is
|
// constructing a `Pin::new()` on a type which does not implement `Unpin` is
|
||||||
// unsafe. A value pinned to heap can be constructed while staying in safe
|
// unsafe. An object pinned to heap can be constructed while staying in safe
|
||||||
// Rust so we can use that to avoid unsafe. You can also use crates like
|
// Rust so we can use that to avoid unsafe. You can also use crates like
|
||||||
// `pin_utils` to pin to the stack safely, just remember that they use
|
// `pin_utils` to pin to the stack safely, just remember that they use
|
||||||
// unsafe under the hood so it's like using an already-reviewed unsafe
|
// unsafe under the hood so it's like using an already-reviewed unsafe
|
||||||
@@ -678,9 +676,9 @@ impl GeneratorA {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tells us that the underlying pointer is not safe to move after pinning.
|
// This tells us that this object is not safe to move after pinning.
|
||||||
// In this case, only we as implementors "feel" this, however, if someone is
|
// In this case, only we as implementors "feel" this, however, if someone is
|
||||||
// relying on our Pinned pointer this will prevent them from moving it. You need
|
// relying on our Pinned data this will prevent them from moving it. You need
|
||||||
// to enable the feature flag `#![feature(optin_builtin_traits)]` and use the
|
// to enable the feature flag `#![feature(optin_builtin_traits)]` and use the
|
||||||
// nightly compiler to implement `!Unpin`. Normally, you would use
|
// nightly compiler to implement `!Unpin`. Normally, you would use
|
||||||
// `std::marker::PhantomPinned` to indicate that the struct is `!Unpin`.
|
// `std::marker::PhantomPinned` to indicate that the struct is `!Unpin`.
|
||||||
|
|||||||
Reference in New Issue
Block a user