The `pin-project` crate in Rust is a procedural macro that provides a safe and ergonomic way to project out of a `Pin<&mut Struct>` reference into `Pin<&mut Field>` or `&mut Field` references. This is a crucial tool when working with self-referential data structures, especially in asynchronous programming (futures, streams) where data often needs to be pinned to memory to ensure its address stability.
What is Pinning?
In Rust, the `Pin<P>` type ensures that the data pointed to by `P` will not be moved or deallocated until it is dropped. This is essential for types that might contain pointers to themselves or to parts of themselves (self-referential types). If such a type were moved, its internal pointers would become invalid, leading to memory unsafety.
The Problem `pin-project` Solves:
When you have a `Pin<&mut Struct>` where `Struct` contains various fields, you often need to access or modify these fields. Directly getting a `&mut Field` from `Pin<&mut Struct>` is generally unsafe if the field itself could be moved independently or if moving the field would invalidate self-references within the `Struct`. More importantly, if a field is itself a `Future` that requires pinning (e.g., `tokio::time::Sleep`), you need a `Pin<&mut MyFutureField>` to safely `poll` it.
Manually implementing safe projections requires careful consideration of Rust's pinning rules, including:
1. `Unpin` Trait: Understanding which fields are `Unpin` (can be moved) and which are not.
2. `Drop` Glue: Ensuring `Drop` implementations are called correctly if a type is partially moved out of.
3. `unsafe` Code: Writing `unsafe` code to perform the projections while upholding pinning invariants.
This manual process is complex and prone to errors. `pin-project` automates this by generating the necessary `unsafe` code, projection structs, and methods safely.
How `pin-project` Works:
1. `#[pin_project]` Attribute: You annotate your struct with `#[pin_project]`. This attribute transforms the struct definition.
2. `#[pin]` Attribute on Fields: For fields that *must* remain pinned when the containing struct is pinned (e.g., inner `Future`s), you mark them with `#[pin]`. These fields will be projected as `Pin<&mut FieldType>`.
3. Generated Projection Struct and Methods: `pin-project` generates:
* A private projection struct (e.g., `__PinProjectProjection`) whose fields are references (`Pin<&mut T>` for `#[pin]` fields, `&mut T` for others) to the original struct's fields.
* Methods like `project()`, `project_ref()`, and `project_replace()` on your struct. When called on a `Pin<&mut Struct>`, these methods return an instance of the generated projection struct.
Benefits:
* Safety: Guarantees that projections are performed correctly according to pinning invariants, preventing memory unsafety.
* Ergonomics: Simplifies the development of complex state machines and self-referential types by abstracting away the low-level `unsafe` pinning logic.
* Automatic `Unpin` Bounds: Infers the correct `Unpin` bounds for generated types, making the struct `Unpin` if all its fields are `Unpin`, or correctly propagates the `!Unpin` status.
* `Drop` Glue: Handles the necessary `Drop` glue code to ensure fields are dropped correctly even after projections.
Use Cases:
* Implementing custom `Future`s, `Stream`s, or other `async` primitives where internal state might be self-referential or contains `Future`s that require pinning.
* Building data structures that need stable addresses for their internal components, e.g., for certain kinds of intrusive data structures or caches.
Example Code
```rust
// Cargo.toml
// [dependencies]
// pin-project = "1"
// tokio = { version = "1", features = ["full"] } # For async runtime example
use pin_project::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
// A dummy inner future for demonstration purposes.
// In a real application, this could be `tokio::time::Sleep` or any other future.
struct MyInnerFuture {
completed_in_next_poll: bool,
}
impl Future for MyInnerFuture {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.completed_in_next_poll {
println!(" -> MyInnerFuture: Ready!");
Poll::Ready(())
} else {
// Mark it to complete next time, and wake up the task.
// This simulates some asynchronous work.
self.as_mut().completed_in_next_poll = true;
cx.waker().wake_by_ref(); // Request a re-poll soon
println!(" -> MyInnerFuture: Pending, will complete next time.");
Poll::Pending
}
}
}
// Define a struct that needs to project its fields safely.
// We use `#[pin_project]` to enable safe field projection.
#[pin_project]
struct MyComplexFuture {
// The `#[pin]` attribute here is crucial.
// It ensures that when `MyComplexFuture` is pinned, `inner_future`
// is also projected as `Pin<&mut MyInnerFuture>`. This is necessary
// for safely calling `poll` on `inner_future` if it requires pinning.
#[pin]
inner_future: MyInnerFuture,
// Fields without `#[pin]` are projected as mutable references (`&mut T`).
message: String,
counter: u8,
}
impl Future for MyComplexFuture {
type Output = String;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// `self.project()` is a method generated by `#[pin_project]`.
// It returns a projection struct whose fields are references
// to the original struct's fields.
//
// `this.inner_future` will be `Pin<&mut MyInnerFuture>` (because of `#[pin]`)
// `this.message` will be `&mut String`
// `this.counter` will be `&mut u8`
let mut this = self.project();
println!("MyComplexFuture polling...");
// Safely poll the inner future using its pinned reference.
match this.inner_future.as_mut().poll(cx) {
Poll::Ready(_) => {
// The inner future has completed.
// We can now finalize `MyComplexFuture`.
*this.counter += 1; // Increment counter via `&mut u8`
println!("MyComplexFuture: Inner future done. Counter: {}", this.counter);
// We can safely move `message` out because it's not pinned.
Poll::Ready(std::mem::take(this.message))
}
Poll::Pending => {
// The inner future is still pending.
// We print a status and return `Pending` ourselves.
println!("MyComplexFuture: Still pending. Message: {}", this.message);
Poll::Pending
}
}
}
}
#[tokio::main] // Using tokio for an easy async runtime to run our future
async fn main() {
let my_future = MyComplexFuture {
inner_future: MyInnerFuture { completed_in_next_poll: false },
message: "Data from complex future!".to_string(),
counter: 0,
};
println!("\n--- Starting MyComplexFuture ---");
let result = my_future.await;
println!("--- MyComplexFuture finished with result: "{}" ---", result);
let another_future = MyComplexFuture {
inner_future: MyInnerFuture { completed_in_next_poll: false },
message: "Second run's data.".to_string(),
counter: 10,
};
println!("\n--- Starting AnotherComplexFuture ---");
let another_result = another_future.await;
println!("--- AnotherComplexFuture finished with result: "{}" ---", another_result);
}
```








pin-project