Pin anyway?It's for self-referential data-structures. Let's try making one. Here we have a struct pointing to itself.
struct MyStruct<'a> {
x: String,
y: &'a String,
}
let s = String::new();
- move occurs because `s` has type `String`, which does not implement the `Copy` trait
let my_struct = MyStruct { x: s, y: &s };
- ^^ value borrowed here after move
|
value moved here
Hmmm, s is moved after we take a reference to it. Let's try.
struct MyStruct<'a> {
x: String,
y: Option<&'a String>,
}
let s = String::new();
let mut my_struct = MyStruct { x: s, y: None };
my_struct.y = Some(&my_struct.x);
It compiles! But as soon as we try to do anything with it, even something as innoculous as calling drop...
error[E0505]: cannot move out of `my_struct` because it is borrowed
drop(my_struct)
^^^^^^^^^
|
move out of `my_struct` occurs here
Why is this a problem? Why won't Rust just let me do it?
Most compile errors in Rust hint at some bigger issue in your program. Let's see what our struct looks like in memory.
.-------------.
v |
.-----------.------------.
| x: String | y: &String |
.-----------.------------.
If we try to call drop, we move the value to a new location in memory and free the old space.
.---------------------------------------------.
v |
.------------------------. .-----------.------------.
| freed | | x: String | y: &String |
.------------------------. .-----------.------------.
But look! When Rust moves a value, it does so bit by bit, copying the data exactly. Our pointer now points to the old location which is now empty! That's UB!
Let's say I'm writing a library, and I have a self-referential struct that uses unsafe raw pointers.
struct MyStruct {
x: String,
y: *const String,
}
This is a problem, if I give my users an &mut MyStruct then they can use std::mem::* functions to move the struct around and cause UB. In Rust, it is true that if you give someone an &mut pointer, they can use it to move the data. How do I give my users MyStruct without letting them move it?
This is where Pin comes in. Pin isn't a special compiler feature. It's actually just a regular struct, and it looks like this.
pub struct Pin<Ptr> {
pointer: Ptr,
}
Yup! That's it!
Instead of giving my users an &mut MyStruct. I can give them a Pin<&mut MyStruct>. If they try to std::mem::take it, they'll get an error!
fn do_bad_stuff(my_struct: Pin<&mut MyStruct>) {
std::mem::take(my_struct);
-------------- ^ expected `&mut _`, found `Pin<&mut MyStruct>`
|
arguments to this function are incorrect
}
In short, Pin guarantees that the value behind a reference will never move.
You can pin a value on the heap with Box::pin. You can pin a value on the stack with the pin! macro. Or if the type implements Unpin you can just use Pin::new. Unpin is an escape hatch that says "hey I know I wrapped this type in a Pin<T> but just disregard that because I actually don't care if you move it.". It's used to call functions that expect a Pin<T> even when pinning T has no effect.
Futures are state-machines. Let's look at an example.
async fn crunch_numbers() {
let x = 1;
println!("Om! {x}");
let y = 2
let z = &y;
println!("Nom! {z}");
}
Rust turns this into something like.
fn crunch_numbers() -> impl Future {
CrunchNumbersStateMachine::Start
}
enum CrunchNumbersStateMachine {
Start,
Om { x: i32 },
// z points to y
Nom { y: i32, z: *const i32 },
Done,
}
Do you see that? Yup, it's a self-referential struct.
Let's look at the function signature for Future::poll.
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
The Pin<&mut Self> just means "hey if you're going to poll the future, please don't move it.". You can imagine it would be pretty bad if a future got half-way through execution, with active references into itself, and then suddenly got moved to another place in memory.
Pin might become obsolete in the future.