Reputation: 8053
I'm writing Rust code which communicates with a C API, and I need a certain struct to have a fixed memory address. So far, I found:
Pin
for pointers;pin_mut
macro to pin on the stack;PhantomPinned
, which is not clear to me.If I understood correctly, PhantomPinned
would be the easiest to use: just having it as a member would make my struct automatically have a fixed memory address. Is this correct?
Upvotes: 3
Views: 1293
Reputation: 70990
What you need is Pin
. It's a pointer that keeps your object from being moved.
pin_mut!()
is just a macro that helps you create a Pin
instance without heap allocation or unsafe
code.
PhantomPinned
has another, though related, goal. By default, your type will implement the Unpin
trait (unless it contains a type that don't implement Unpin
, however most types do). This is problematic, because for types that implements Unpin
, Pin
has no effect: it does not prevent you from moving them. This is because most types do not need a fixed memory address. Think about i32
, for example: even if you create Pin<&mut i32>
, you can move it, because i32
implements Unpin
, because even if you'll move it nothing bad will happen, and no data will be validated.
So for Pin
to be effective you need to un-impl Unpin
. This is done by either including a field that doesn't implements Unpin
itself as a field, or having a negative impl (see What is an auto trait in Rust?):
impl !Unpin for MyType {}
Unfortunately, negative impls are unstable. Instead, the standard library provides the PhantomPinned
type, which is costless (a ZST), but doesn't implement Unpin
(it has a negative impl, since the standard library is allowed to use unstable features it can do that). And now when your type includes it it becomes !Unpin
too.
To sum up, you need to include PhantomPinned
in your struct so it will not be Unpin
, and you need to wrap it in Pin
so it will be unmovable.
Upvotes: 6
Reputation: 26569
The only unmoveable value in Rust is the value in Pin<T>
and only if T
does not implement Unpin
.
T
may be a reference or some smart pointer type like Box
or Arc
, but Pin
is needed to specify that the value may not move.
Unpin
is a trait implemented for types that don't care if they are moved, even if they are pinned. It's a marker trait automatically implemented for all types whose members all implement Unpin
. PhantomPinned
is a simple, zero sized type that does not implement Unpin
, so it may be added to a struct or enum to make it not automatically implement Unpin
.
Note that Unpin
is only relevant to Pin
wrapped values - values outside of Pin
can still be moved. Operations requiring the value not be moved should take a Pin<&mut T>
instead of &mut T
.
pin_mut
is a macro for converting a stack value to a pinned pointer. It's just a convenience for a known-safe case and doesn't really play into how Pin
works.
So for your case, it sounds like your structure should have a PhantomPinned
member, and all of its operations should take a pin-wrapped reference.
Upvotes: 1