mirror of https://github.com/rust-lang/async-book
Expanding Pin chapter + more precise wording as suggested in #73
This commit is contained in:
parent
d073a9dea7
commit
afb8b4c5b8
|
@ -2,15 +2,16 @@
|
||||||
|
|
||||||
To poll futures, they must be pinned using a special type called
|
To poll futures, they must be pinned using a special type called
|
||||||
`Pin<T>`. If you read the explanation of [the `Future` trait] in the
|
`Pin<T>`. If you read the explanation of [the `Future` trait] in the
|
||||||
previous section ["Executing `Future`s and Tasks"], you'll recognise
|
previous section ["Executing `Future`s and Tasks"], you'll recognize
|
||||||
`Pin` from the `self: Pin<&mut Self>` in the `Future::poll` method's definition.
|
`Pin` from the `self: Pin<&mut Self>` in the `Future::poll` method's definition.
|
||||||
But what does it mean, and why do we need it?
|
But what does it mean, and why do we need it?
|
||||||
|
|
||||||
## Why Pinning
|
## Why Pinning
|
||||||
|
|
||||||
Pinning makes it possible to guarantee that an object won't ever be moved.
|
`Pin` works in tandem with the `Unpin` marker. Pinning makes it possible
|
||||||
To understand why this is necessary, we need to remember how `async`/`.await`
|
to guarantee that an object implmenting `!Unpin` won't ever be moved. To understand
|
||||||
works. Consider the following code:
|
why this is necessary, we need to remember how `async`/`.await` works. Consider
|
||||||
|
the following code:
|
||||||
|
|
||||||
```rust,edition2018,ignore
|
```rust,edition2018,ignore
|
||||||
let fut_one = /* ... */;
|
let fut_one = /* ... */;
|
||||||
|
@ -98,17 +99,490 @@ move as well, invalidating the pointer stored in `read_into_buf_fut.buf`.
|
||||||
Pinning futures to a particular spot in memory prevents this problem, making
|
Pinning futures to a particular spot in memory prevents this problem, making
|
||||||
it safe to create references to values inside an `async` block.
|
it safe to create references to values inside an `async` block.
|
||||||
|
|
||||||
## How to Use Pinning
|
## Pinning in Detail
|
||||||
|
|
||||||
|
Let's try to understand pinning by using an slightly simpler example. The problem we encounter
|
||||||
|
above is a problem that ultimately boils down to how we handle references in self-referential
|
||||||
|
types in Rust.
|
||||||
|
|
||||||
|
For now our example will look like this:
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Test {
|
||||||
|
a: String,
|
||||||
|
b: *const String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
fn new(txt: &str) -> Self {
|
||||||
|
Test {
|
||||||
|
a: String::from(txt),
|
||||||
|
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)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Test` provides methods to get a reference to the value of the fields `a` and `b`. Since `b` is a
|
||||||
|
reference to `a` we store it as a pointer since the borrowing rules of Rust doesn't allow us to
|
||||||
|
define this lifetime. We now have what we call a self-referential struct.
|
||||||
|
|
||||||
|
Our example works fine if we don't move any of our data around as you can observe by running
|
||||||
|
this example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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());
|
||||||
|
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||||
|
|
||||||
|
}
|
||||||
|
# use std::pin::Pin;
|
||||||
|
# #[derive(Debug)]
|
||||||
|
# struct Test {
|
||||||
|
# a: String,
|
||||||
|
# b: *const String,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# impl Test {
|
||||||
|
# fn new(txt: &str) -> Self {
|
||||||
|
# Test {
|
||||||
|
# a: String::from(txt),
|
||||||
|
# b: std::ptr::null(),
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# // We need an `init` method to actually set our self-reference
|
||||||
|
# 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)}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
We get what we'd expect:
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
a: test1, b: test1
|
||||||
|
a: test2, b: test2
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's see what happens if we swap `test1` with `test2` and thereby move the data:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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);
|
||||||
|
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||||
|
|
||||||
|
}
|
||||||
|
# use std::pin::Pin;
|
||||||
|
# #[derive(Debug)]
|
||||||
|
# struct Test {
|
||||||
|
# a: String,
|
||||||
|
# b: *const String,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# impl Test {
|
||||||
|
# fn new(txt: &str) -> Self {
|
||||||
|
# Test {
|
||||||
|
# a: String::from(txt),
|
||||||
|
# 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)}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
Naively, we could think that what we should get a debug print of `test1` two times like this:
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
a: test1, b: test1
|
||||||
|
a: test1, b: test1
|
||||||
|
```
|
||||||
|
|
||||||
|
But instead we get:
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
a: test1, b: test1
|
||||||
|
a: test1, b: test2
|
||||||
|
```
|
||||||
|
|
||||||
|
The pointer to `test2.b` still points to the old location which is inside `test1`
|
||||||
|
now. The struct is not self-referential anymore, it holds a pointer to a field
|
||||||
|
in a different object. That means we can't rely on the lifetime of `test2.b` to
|
||||||
|
be tied to the lifetime of `test2` anymore.
|
||||||
|
|
||||||
|
If your still not convinced, this should at least convince you:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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);
|
||||||
|
test1.a = "I've totally changed now!".to_string();
|
||||||
|
println!("a: {}, b: {}", test2.a(), test2.b());
|
||||||
|
|
||||||
|
}
|
||||||
|
# use std::pin::Pin;
|
||||||
|
# #[derive(Debug)]
|
||||||
|
# struct Test {
|
||||||
|
# a: String,
|
||||||
|
# b: *const String,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# impl Test {
|
||||||
|
# fn new(txt: &str) -> Self {
|
||||||
|
# Test {
|
||||||
|
# a: String::from(txt),
|
||||||
|
# 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)}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
The diagram below can help visualize what's going on:
|
||||||
|
|
||||||
|
**Fig 1: Before and after swap**
|
||||||
|
![swap_problem](../assets/swap_problem.jpg)
|
||||||
|
|
||||||
|
It's easy to get this to show UB and fail in other spectacular ways as well.
|
||||||
|
|
||||||
|
## Pinning in Practice
|
||||||
|
|
||||||
|
Let's see how pinning and the `Pin` type can help us solve this problem.
|
||||||
|
|
||||||
The `Pin` type wraps pointer types, guaranteeing that the values behind the
|
The `Pin` type wraps pointer types, guaranteeing that the values behind the
|
||||||
pointer won't be moved. For example, `Pin<&mut T>`, `Pin<&T>`,
|
pointer won't be moved. For example, `Pin<&mut T>`, `Pin<&T>`,
|
||||||
`Pin<Box<T>>` all guarantee that `T` won't be moved.
|
`Pin<Box<T>>` all guarantee that `T` won't be moved if `T: !Unpin`.
|
||||||
|
|
||||||
Most types don't have a problem being moved. These types implement a trait
|
Most types don't have a problem being moved. These types implement a trait
|
||||||
called `Unpin`. Pointers to `Unpin` types can be freely placed into or taken
|
called `Unpin`. Pointers to `Unpin` types can be freely placed into or taken
|
||||||
out of `Pin`. For example, `u8` is `Unpin`, so `Pin<&mut u8>` behaves just like
|
out of `Pin`. For example, `u8` is `Unpin`, so `Pin<&mut u8>` behaves just like
|
||||||
a normal `&mut u8`.
|
a normal `&mut u8`.
|
||||||
|
|
||||||
|
However, types that can't be moved after they're pinned has a marker called
|
||||||
|
`!Unpin`. Futures created by async/await is an example of this.
|
||||||
|
|
||||||
|
### Pinning to the Stack
|
||||||
|
|
||||||
|
Back to our example. We can solve our problem by using `Pin`. Let's take a look at what
|
||||||
|
our example would look like we required a pinned pointer instead:
|
||||||
|
|
||||||
|
```rust, ignore
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Test {
|
||||||
|
a: String,
|
||||||
|
b: *const String,
|
||||||
|
_marker: PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
fn new(txt: &str) -> Self {
|
||||||
|
Test {
|
||||||
|
a: String::from(txt),
|
||||||
|
b: std::ptr::null(),
|
||||||
|
_marker: PhantomPinned, // This makes our type `!Unpin`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn init<'a>(self: Pin<&'a mut Self>) {
|
||||||
|
let self_ptr: *const String = &self.a;
|
||||||
|
let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
this.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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Pinning an object to the stack will always be `unsafe` if our type implements
|
||||||
|
`!Unpin`. You can use a crate like [`pin_utils`][pin_utils] to avoid writing
|
||||||
|
our own `unsafe` code when pinning to the stack.
|
||||||
|
|
||||||
|
Below, we pin the objects `test1` and `test2` to the stack:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn main() {
|
||||||
|
// test1 is safe to move before we initialize it
|
||||||
|
let mut test1 = Test::new("test1");
|
||||||
|
// Notice how we shadow `test1` to prevent it from being accessed again
|
||||||
|
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||||
|
Test::init(test1.as_mut());
|
||||||
|
|
||||||
|
let mut test2 = Test::new("test2");
|
||||||
|
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||||
|
Test::init(test2.as_mut());
|
||||||
|
|
||||||
|
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;
|
||||||
|
#
|
||||||
|
# #[derive(Debug)]
|
||||||
|
# struct Test {
|
||||||
|
# a: String,
|
||||||
|
# b: *const String,
|
||||||
|
# _marker: PhantomPinned,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# impl Test {
|
||||||
|
# fn new(txt: &str) -> Self {
|
||||||
|
# Test {
|
||||||
|
# a: String::from(txt),
|
||||||
|
# b: std::ptr::null(),
|
||||||
|
# // This makes our type `!Unpin`
|
||||||
|
# _marker: PhantomPinned,
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||||
|
# let self_ptr: *const String = &self.a;
|
||||||
|
# let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
# this.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) }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, if we try to move our data now we get a compilation error:
|
||||||
|
|
||||||
|
```rust, compile_fail
|
||||||
|
pub fn main() {
|
||||||
|
let mut test1 = Test::new("test1");
|
||||||
|
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||||
|
Test::init(test1.as_mut());
|
||||||
|
|
||||||
|
let mut test2 = Test::new("test2");
|
||||||
|
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||||
|
Test::init(test2.as_mut());
|
||||||
|
|
||||||
|
println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
|
||||||
|
std::mem::swap(test1.get_mut(), test2.get_mut());
|
||||||
|
println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
|
||||||
|
}
|
||||||
|
# use std::pin::Pin;
|
||||||
|
# use std::marker::PhantomPinned;
|
||||||
|
#
|
||||||
|
# #[derive(Debug)]
|
||||||
|
# struct Test {
|
||||||
|
# a: String,
|
||||||
|
# b: *const String,
|
||||||
|
# _marker: PhantomPinned,
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# impl Test {
|
||||||
|
# fn new(txt: &str) -> Self {
|
||||||
|
# Test {
|
||||||
|
# a: String::from(txt),
|
||||||
|
# b: std::ptr::null(),
|
||||||
|
# _marker: PhantomPinned, // This makes our type `!Unpin`
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# fn init<'a>(self: Pin<&'a mut Self>) {
|
||||||
|
# let self_ptr: *const String = &self.a;
|
||||||
|
# let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
# this.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) }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
```
|
||||||
|
|
||||||
|
The type system prevents us from moving the data.
|
||||||
|
|
||||||
|
> It's important to note that stack pinning will always rely on guarantees
|
||||||
|
> you give when writing `unsafe`. While we know that the _pointee_ of `&'a mut T`
|
||||||
|
> is pinned for the lifetime of `'a` we can't know if the data `&'a mut T`
|
||||||
|
> points to isn't moved after `'a` ends. If it does it will violate the Pin
|
||||||
|
> contract.
|
||||||
|
>
|
||||||
|
> A mistake that is easy to make is forgetting to shadow the original variable
|
||||||
|
> since you could drop the `Pin` and move the data after `&'a mut T`
|
||||||
|
> like shown below (which violates the Pin contract):
|
||||||
|
>
|
||||||
|
> ```rust
|
||||||
|
> fn main() {
|
||||||
|
> 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!(r#"test1.b points to "test1": {:?}..."#, test1.b);
|
||||||
|
> let mut test2 = Test::new("test2");
|
||||||
|
> mem::swap(&mut test1, &mut test2);
|
||||||
|
> println!("... and now it points nowhere: {:?}", test1.b);
|
||||||
|
> }
|
||||||
|
> # use std::pin::Pin;
|
||||||
|
> # use std::marker::PhantomPinned;
|
||||||
|
> # use std::mem;
|
||||||
|
> #
|
||||||
|
> # #[derive(Debug)]
|
||||||
|
> # struct Test {
|
||||||
|
> # a: String,
|
||||||
|
> # b: *const String,
|
||||||
|
> # _marker: PhantomPinned,
|
||||||
|
> # }
|
||||||
|
> #
|
||||||
|
> #
|
||||||
|
> # impl Test {
|
||||||
|
> # fn new(txt: &str) -> Self {
|
||||||
|
> # Test {
|
||||||
|
> # a: String::from(txt),
|
||||||
|
> # b: std::ptr::null(),
|
||||||
|
> # // This makes our type `!Unpin`
|
||||||
|
> # _marker: PhantomPinned,
|
||||||
|
> # }
|
||||||
|
> # }
|
||||||
|
> # fn init<'a>(self: Pin<&'a mut Self>) {
|
||||||
|
> # let self_ptr: *const String = &self.a;
|
||||||
|
> # let this = unsafe { self.get_unchecked_mut() };
|
||||||
|
> # this.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) }
|
||||||
|
> # }
|
||||||
|
> # }
|
||||||
|
> ```
|
||||||
|
|
||||||
|
### Pinning to the Heap
|
||||||
|
|
||||||
|
Pinning an `!Unpin` type to the heap gives our data a stable address so we know
|
||||||
|
that the data we point to can't move after it's pinned. In contrast to stack
|
||||||
|
pinning, we know that the data will be pinned for the lifetime of the object.
|
||||||
|
|
||||||
|
```rust, edition2018
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Test {
|
||||||
|
a: String,
|
||||||
|
b: *const String,
|
||||||
|
_marker: PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
fn new(txt: &str) -> Pin<Box<Self>> {
|
||||||
|
let t = Test {
|
||||||
|
a: String::from(txt),
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Some functions require the futures they work with to be `Unpin`. To use a
|
Some functions require the futures they work with to be `Unpin`. To use a
|
||||||
`Future` or `Stream` that isn't `Unpin` with a function that requires
|
`Future` or `Stream` that isn't `Unpin` with a function that requires
|
||||||
`Unpin` types, you'll first have to pin the value using either
|
`Unpin` types, you'll first have to pin the value using either
|
||||||
|
@ -138,5 +612,30 @@ pin_mut!(fut);
|
||||||
execute_unpin_future(fut); // OK
|
execute_unpin_future(fut); // OK
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
1. If `T: Unpin` (which is the default), then `Pin<'a, T>` is entirely
|
||||||
|
equivalent to `&'a mut T`. in other words: `Unpin` means it's OK for this type
|
||||||
|
to be moved even when pinned, so `Pin` will have no effect on such a type.
|
||||||
|
|
||||||
|
2. Getting a `&mut T` to a pinned T requires unsafe if `T: !Unpin`.
|
||||||
|
|
||||||
|
3. Most standard library types implement `Unpin`. The same goes for most
|
||||||
|
"normal" types you encounter in Rust. A `Future` generated by async/await is an exception to this rule.
|
||||||
|
|
||||||
|
4. 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.
|
||||||
|
|
||||||
|
5. You can either pin data to the stack or to the heap.
|
||||||
|
|
||||||
|
6. Pinning a `!Unpin` object to the stack requires `unsafe`
|
||||||
|
|
||||||
|
7. Pinning a `!Unpin` object to the heap does not require `unsafe`. There is a shortcut for doing this using `Box::pin`.
|
||||||
|
|
||||||
|
8. For pinned data where `T: !Unpin` you have to maintain the invariant that its memory will not
|
||||||
|
get invalidated or repurposed _from the moment it gets pinned until when drop_ is called. This is
|
||||||
|
an important part of the _pin contract_.
|
||||||
|
|
||||||
["Executing `Future`s and Tasks"]: ../02_execution/01_chapter.md
|
["Executing `Future`s and Tasks"]: ../02_execution/01_chapter.md
|
||||||
[the `Future` trait]: ../02_execution/02_future.md
|
[the `Future` trait]: ../02_execution/02_future.md
|
||||||
|
[pin_utils]: https://docs.rs/pin-utils/
|
Binary file not shown.
After Width: | Height: | Size: 165 KiB |
Loading…
Reference in New Issue