mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-06 08:49:17 +02:00
Better explanation why futures need to be pinned (#1687)
Attempt to address #1677. Expert review is needed. The new text is my best guess based on the original text and other explanations I could find online. A few things to note: * I'm trying to distinguish the future we return and the future we await. My assumption is that the stack contents goes to the future the code returns, not the future the code is awaiting. * Readers could be worried if they need to pin the code they write. I'm reassuring them that the borrow checks would normally catch bad references. * I'm intentionally avoiding the words that something is unsafe (or would be unsafe). The async Rust is safe. * I'm trying to be clear that `Pin` is a protective wrapper around a pointer, not a mechanism that changes the pointer or the pointed object. * Likewise, I don't want to give an impression that an unpinned pointer to a future is inherently unsafe or invalid. It just cannot be used to poll the future. * I dropped the vague mention of the "issues", as it probably refers to the issue with replacing a future (as opposed to resetting it in place). It's already mentioned in the notes further on this page. It affects pinning on stack only, `Box::pin()` can be replaced. Co-authored-by: Martin Geisler <martin@geisler.net>
This commit is contained in:
parent
f23aa10d32
commit
0a1c30ef87
@ -1,13 +1,17 @@
|
||||
# `Pin`
|
||||
|
||||
When you await a future, all local variables (that would ordinarily be stored on
|
||||
a stack frame) are instead stored in the Future for the current async block. If
|
||||
your future has pointers to data on the stack, those pointers might get
|
||||
invalidated. This is unsafe.
|
||||
Async blocks and functions return types implementing the `Future` trait. The
|
||||
type returned is the result of a compiler transformation which turns local
|
||||
variables into data stored inside the future.
|
||||
|
||||
Therefore, you must guarantee that the addresses your future points to don't
|
||||
change. That is why we need to "pin" futures. Using the same future repeatedly
|
||||
in a `select!` often leads to issues with pinned values.
|
||||
Some of those variables can hold pointers to other local variables. Because of
|
||||
that, the future should never be moved to a different memory location, as it
|
||||
would invalidate those pointers.
|
||||
|
||||
To prevent moving the future type in memory, it can only be polled through a
|
||||
pinned pointer. `Pin` is a wrapper around a reference that disallows all
|
||||
operations that would move the instance it points to into a different memory
|
||||
location.
|
||||
|
||||
```rust,editable,compile_fail
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
@ -107,4 +111,18 @@ async fn main() {
|
||||
- Another alternative is to not use `pin` at all but spawn another task that
|
||||
will send to a `oneshot` channel every 100ms.
|
||||
|
||||
- Data that contains pointers to itself is called self-referential. Normally,
|
||||
the Rust borrow checker would prevent self-referential data from being moved,
|
||||
as the references cannot outlive the data they point to. However, the code
|
||||
transformation for async blocks and functions is not verified by the borrow
|
||||
checker.
|
||||
|
||||
- `Pin` is a wrapper around a reference. An object cannot be moved from its
|
||||
place using a pinned pointer. However, it can still be moved through an
|
||||
unpinned pointer.
|
||||
|
||||
- The `poll` method of the `Future` trait uses `Pin<&mut Self>` instead of
|
||||
`&mut Self` to refer to the instance. That's why it can only be called on a
|
||||
pinned pointer.
|
||||
|
||||
</details>
|
||||
|
Loading…
x
Reference in New Issue
Block a user