Expand description
Types that pin data to a location in memory.
It is sometimes useful to be able to rely upon a certain value not being able tomove,in the sense that its address in memory cannot change. This is useful especially when thereare one or morepointers pointing at that value. The ability to rely on thisguarantee that the value apointer is pointing at (itspointee) will
- Not bemoved out of its memory location
- More generally, remainvalid at that same memory location
is called “pinning.” We would say that a value which satisfies these guarantees has been“pinned,” in that it has been permanently (until the end of its lifespan) attached to itslocation in memory, as though pinned to a pinboard. Pinning a value is an incredibly usefulbuilding block forunsafe code to be able to reason about whether a raw pointer to thepinned value is still valid.As we’ll see later, once a value is pinned,it is necessarily valid at its memory location until the end of its lifespan. This conceptof “pinning” is necessary to implement safe interfaces on top of things like self-referentialtypes and intrusive data structures which cannot currently be modeled in fully safe Rust usingonly borrow-checkedreferences.
“Pinning” allows us to put avalue which exists at some location in memory into a state wheresafe code cannotmove that value to a different location in memory or otherwise invalidate itat its current location (unless it implementsUnpin, which we willtalk about below). Anything that wants to interact with the pinned value in a waythat has the potential to violate these guarantees must promise that it will not actuallyviolate them, using theunsafe keyword to mark that such a promise is upheld by the userand not the compiler. In this way, we can allow otherunsafe code to rely on any pointersthat point to the pinned value to be valid to dereference while it is pinned.
Note that as long as you don’t useunsafe, it’s impossible to create or misuse a pinnedvalue in a way that is unsound. See the documentation ofPin<Ptr> for moreinformation on the practicalities of how to pin a value and how to use that pinned value from auser’s perspective without usingunsafe.
The rest of this documentation is intended to be the source of truth for users ofPin<Ptr>that are implementing theunsafe pieces of an interface that relies on pinning for validity;users ofPin<Ptr> in safe code do not need to read it in detail.
There are several sections to this documentation:
- What is “moving”?
- What is “pinning”?
- Address sensitivity, AKA “when do we need pinning?”
- Examples of types with address-sensitive states
- Subtle details and the
Dropguarantee
§What is “moving”?
When we say a value ismoved, we mean that the compiler copies, byte-for-byte, thevalue from one location to another. In a purely mechanical sense, this is identical toCopying a value from one place in memory to another. In Rust, “move” carries with it thesemantics of ownership transfer from one variable to another, which is the key differencebetween aCopy and a move. For the purposes of this module’s documentation, however, whenwe writemove in italics, we meanspecifically that the value hasmoved in the mechanicalsense of being located at a new place in memory.
All values in Rust are triviallymoveable. This means that the address at which a value islocated is not necessarily stable in between borrows. The compiler is allowed tomove a valueto a new address without running any code to notify that value that its addresshas changed. Although the compiler will not insert memorymoves where no semantic move hasoccurred, there are many places where a valuemay be moved. For example, when doingassignment or passing a value into a function.
#[derive(Default)]structAddrTracker(Option<usize>);implAddrTracker {// If we haven't checked the addr of self yet, store the current // address. If we have, confirm that the current address is the same // as it was last time, or else panic.fncheck_for_move(&mutself) {letcurrent_addr =selfas*mutSelfasusize;matchself.0{None=>self.0=Some(current_addr),Some(prev_addr) =>assert_eq!(prev_addr, current_addr), } }}// Create a tracker and store the initial addressletmuttracker = AddrTracker::default();tracker.check_for_move();// Here we shadow the variable. This carries a semantic move, and may therefore also// come with a mechanical memory *move*letmuttracker = tracker;// May panic!// tracker.check_for_move();In this sense, Rust does not guarantee thatcheck_for_move() will never panic, because thecompiler is permitted tomovetracker in many situations.
Common smart-pointer types such asBox<T> and&mut T also allowmoving the underlyingvalue they point at: you can move out of aBox<T>, or you can usemem::replace tomove aT out of a&mut T. Therefore, putting a value (such astracker above) behind apointer isn’t enough on its own to ensure that its address does not change.
§What is “pinning”?
We say that a value has beenpinned when it has been put into a state where it is guaranteedto remainlocated at the same place in memory from the time it is pinned until itsdrop is called.
§Address-sensitive values, AKA “when we need pinning”
Most values in Rust are entirely okay with beingmoved around at-will.Types for which it isalways the case thatany value of that type can bemoved at-will should implementUnpin, which we will discuss morebelow.
Pin is specifically targeted at allowing the implementation ofsafe interfaces aroundtypes which have some state during which they become “address-sensitive.” A value in such an“address-sensitive” state isnot okay with beingmoved around at-will. Such a value muststayun-moved and valid during the address-sensitive portion of its lifespan because someinterface is relying on those invariants to be true in order for its implementation to be sound.
As a motivating example of a type which may become address-sensitive, consider a type whichcontains a pointer to another piece of its own data,i.e. a “self-referential” type. In orderfor such a type to be implemented soundly, the pointer which points intoself’s data must beproven valid whenever it is accessed. But if that value ismoved, the pointer will stillpoint to the old address where the value was located and not into the new location ofself,thus becoming invalid. A key example of such self-referential types are the state machinesgenerated by the compiler to implementFuture forasync fns.
Such types that have anaddress-sensitive state usually follow a lifecyclethat looks something like so:
- A value is created which can be freely moved around.
- e.g. calling an async function which returns a state machine implementing
Future
- e.g. calling an async function which returns a state machine implementing
- An operation causes the value to depend on its own address not changing
- Further pieces of the safe interface of the type use internal
unsafeoperations whichassume that the address of the value is stable- e.g. subsequent calls to
poll
- e.g. subsequent calls to
- Before the value is invalidated (e.g. deallocated), it isdropped, giving it a chance tonotify anything with pointers to itself that those pointers will be invalidated
There are two possible ways to ensure the invariants required for 2. and 3. above (whichapply to any address-sensitive type, not just self-referential types) do not get broken.
- Have the value detect when it is moved and update all the pointers that point to itself.
- Guarantee that the address of the value does not change (and that memory is not re-usedfor anything else) during the time that the pointers to it are expected to be valid todereference.
Since, as we discussed, Rust can move values without notifying them that they have moved, thefirst option is ruled out.
In order to implement the second option, we must in some way enforce its key invariant,i.e. prevent the value from beingmoved or otherwise invalidated (you may notice thissounds an awful lot like the definition ofpinning a value). There are a few ways one mightbe able to enforce this invariant in Rust:
- Offer a wholly
unsafeAPI to interact with the object, thus requiring every caller touphold the invariant themselves - Store the value that must not be moved behind a carefully managed pointer internal tothe object
- Leverage the type system to encode and enforce this invariant by presenting a restrictedAPI surface to interact withany object that requires these invariants
The first option is quite obviously undesirable, as theunsafety of the interface willbecome viral throughout all code that interacts with the object.
The second option is a viable solution to the problem for some use cases, in particularfor self-referential types. Under this model, any type that has an address sensitive statewould ultimately store its data in something like aBox<T>, carefully manage internalaccess to that data to ensure nomoves or other invalidation occurs, and finallyprovide a safe interface on top.
There are a couple of linked disadvantages to using this model. The most significant is thateach individual object must assume it ison its own to ensurethat its data does not becomemoved or otherwise invalidated. Since there is no sharedcontract between values of different types, an object cannot assume that others interactingwith it will properly respect the invariants around interacting with its data and musttherefore protect it from everyone. Because of this,composition of address-sensitive typesrequires at least a level of pointer indirection each time a new object is added to the mix(and, practically, a heap allocation).
Although there were other reasons as well, this issue of expensive composition is the key thingthat drove Rust towards adopting a different model. It is particularly a problemwhen one considers, for example, the implications of composing together theFutures whichwill eventually make up an asynchronous task (including address-sensitiveasync fn statemachines). It is plausible that there could be many layers ofFutures composed together,including multiple layers ofasync fns handling different parts of a task. It was deemedunacceptable to force indirection and allocation for each layer of composition in this case.
Pin<Ptr> is an implementation of the third option. It allows us to solve the issuesdiscussed with the second option by building ashared contractual language around theguarantees of “pinning” data.
§UsingPin<Ptr> to pin values
In order to pin a value, we wrap apointer to that value (of some typePtr) in aPin<Ptr>.Pin<Ptr> can wrap any pointer type, forming a promise that thepointeewill not bemoved orotherwise invalidated.
We call such aPin-wrapped pointer apinning pointer, (or pinning reference, or pinningBox, etc.) because its existence is the thing that is conceptually pinning the underlyingpointee in place: it is the metaphorical “pin” securing the data in place on the pinboard(in memory).
Notice that the thing wrapped byPin is not the value which we want to pin itself, butrather a pointer to that value! APin<Ptr> does not pin thePtr; instead, it pins thepointer’spointee value.
§Pinning as a library contract
Pinning does not require nor make use of any compiler “magic”2, only a specificcontract between theunsafe parts of a library API and its users.
It is important to stress this point as a user of theunsafe parts of thePin API.Practically, this means that performing the mechanics of “pinning” a value by creating aPin<Ptr> to itdoes not actually change the way the compiler behaves towards theinner value! It is possible to use incorrectunsafe code to create aPin<Ptr> to avalue which does not actually satisfy the invariants that a pinned value must satisfy, and inthis way lead to undefined behavior even in (from that point) fully safe code. Similarly, usingunsafe, one may get access to a bare&mut T from aPin<Ptr> anduse that to invalidlymove the pinned value out. It is the job of the user of theunsafe parts of thePin API to ensure these invariants are not violated.
This differs from e.g.UnsafeCell which changes the semantics of a program’s compiledoutput. APin<Ptr> is a handle to a value which we have promised we will not move out of,but Rust still considers all values themselves to be fundamentally moveable through,e.g.assignment ormem::replace.
§HowPin prevents misuse in safe code
In order to accomplish the goal of pinning the pointee value,Pin<Ptr> restricts access tothe wrappedPtr type in safe code. Specifically,Pin disallows the ability to accessthe wrapped pointer in ways that would allow the user tomove the underlying pointee value orotherwise re-use that memory for something else without usingunsafe. For example, aPin<&mut T> makes it impossible to obtain the wrapped&mut T safely becausethrough that&mut T it would be possible tomove the underlying value out ofthe pointer withmem::replace, etc.
As discussed above, this promise must be upheld manually byunsafe code which interactswith thePin<Ptr> so that otherunsafe code can rely on the pointee value beingun-moved and valid. Interfaces that operate on values which are in an address-sensitive stateaccept an argument likePin<&mut T> orPin<Box<T>> toindicate this contract to the caller.
As discussed below, opting in to using pinning guarantees in the interfaceof an address-sensitive type has consequences for the implementation of some safe traits onthat type as well.
§Interaction betweenDeref andPin<Ptr>
SincePin<Ptr> can wrap any pointer type, it usesDeref andDerefMut inorder to identify the type of the pinned pointee data and provide (restricted) access to it.
APin<Ptr> wherePtr: Deref is a “Ptr-style pinning pointer” to a pinnedPtr::Target – so, aPin<Box<T>> is an owned, pinning pointer to apinnedT, and aPin<Rc<T>> is a reference-counted, pinning pointer to apinnedT.
Pin<Ptr> also uses the<Ptr as Deref>::Target type information to modify theinterface it is allowed to provide for interacting with that data (for example, when apinning pointer points at pinned data which implementsUnpin, asdiscussed below).
Pin<Ptr> requires that implementations ofDeref andDerefMut onPtr return apointer to the pinned data directly and do notmove out of theself parameter during theirimplementation ofDerefMut::deref_mut. It is unsound forunsafe code to wrap pointertypes with such “malicious” implementations ofDeref; seePin<Ptr>::new_unchecked fordetails.
§FixingAddrTracker
The guarantee of a stable address is necessary to make ourAddrTracker example work. Whencheck_for_move sees aPin<&mut AddrTracker>, it can safely assume that valuewill exist at that same address until said value goes out of scope, and thus multiple callsto itcannot panic.
usestd::marker::PhantomPinned;usestd::pin::Pin;usestd::pin::pin;#[derive(Default)]structAddrTracker { prev_addr:Option<usize>,// remove auto-implemented `Unpin` bound to mark this type as having some // address-sensitive state. This is essential for our expected pinning // guarantees to work, and is discussed more below._pin: PhantomPinned,}implAddrTracker {fncheck_for_move(self: Pin<&mutSelf>) {letcurrent_addr =&*selfas*constSelfasusize;matchself.prev_addr {None=> {// SAFETY: we do not move out of selfletself_data_mut =unsafe{self.get_unchecked_mut() }; self_data_mut.prev_addr =Some(current_addr); },Some(prev_addr) =>assert_eq!(prev_addr, current_addr), } }}// 1. Create the value, not yet in an address-sensitive statelettracker = AddrTracker::default();// 2. Pin the value by putting it behind a pinning pointer, thus putting// it into an address-sensitive stateletmutptr_to_pinned_tracker: Pin<&mutAddrTracker> =pin!(tracker);ptr_to_pinned_tracker.as_mut().check_for_move();// Trying to access `tracker` or pass `ptr_to_pinned_tracker` to anything that requires// mutable access to a non-pinned version of it will no longer compile// 3. We can now assume that the tracker value will never be moved, thus// this will never panic!ptr_to_pinned_tracker.as_mut().check_for_move();Note that this invariant is enforced by simply making it impossible to call code that wouldperform a move on the pinned value. This is the case since the only way to access that pinnedvalue is through the pinningPin<&mut T>, which in turn restricts our access.
§Unpin
The vast majority of Rust types have no address-sensitive states. These typesimplement theUnpin auto-trait, which cancels the restrictive effects ofPin when thepointee typeT isUnpin. WhenT: Unpin,Pin<Box<T>> functions identically to a non-pinningBox<T>; similarly,Pin<&mut T> would impose no additional restrictions above a regular&mut T.
The idea of this trait is to alleviate the reduced ergonomics of APIs that require the useofPin for soundness for some types, but which also want to be used by other types thatdon’t care about pinning. The prime example of such an API isFuture::poll. There are manyFuture types that don’t care about pinning. These futures can implementUnpin andtherefore get around the pinning related restrictions in the API, while still allowing thesubset ofFutures whichdo require pinning to be implemented soundly.
Note that the interaction between aPin<Ptr> andUnpin is through the type of thepointee value,<Ptr as Deref>::Target. Whether thePtr type itselfimplementsUnpin does not affect the behavior of aPin<Ptr>. For example, whether or notBox isUnpin has no effect on the behavior ofPin<Box<T>>, becauseT is the type of the pointee value, notBox. So, whetherT implementsUnpin isthe thing that will affect the behavior of thePin<Box<T>>.
Builtin types that areUnpin include all of the primitive types, likebool,i32,andf32, references (&T and&mut T), etc., as well as manycore and standard library types likeBox<T>,String, and more.These types are markedUnpin because they do not have an address-sensitive state like theones we discussed above. If they did have such a state, those parts of their interface would beunsound without being expressed through pinning, and they would then need to notimplementUnpin.
The compiler is free to take the conservative stance of marking types asUnpin so long asall of the types that compose its fields are alsoUnpin. This is because if a typeimplementsUnpin, then it is unsound for that type’s implementation to rely onpinning-related guarantees for soundness,even when viewed through a “pinning” pointer! It isthe responsibility of the implementor of a type that relies upon pinning for soundness toensure that type isnot marked asUnpin by addingPhantomPinned field. This isexactly what we did with ourAddrTracker example above. Without doing this, youmust notrely on pinning-related guarantees to apply to your type!
If you really need to pin a value of a foreign or built-in type that implementsUnpin,you’ll need to create your own wrapper type around theUnpin type you want to pin and thenopt-out ofUnpin usingPhantomPinned.
Exposing access to the inner field which you want to remain pinned must then be carefullyconsidered as well! Remember, exposing a method that gives access to aPin<&mut InnerT> whereInnerT:Unpin would allow safe code totrivially move the inner value out of that pinning pointer, which is precisely what you’reseeking to prevent! Exposing a field of a pinned value through a pinning pointer is called“projecting” a pin, and the more general case of deciding in which cases a pin should be ableto be projected or not is called “structural pinning.” We will go into more detail about thisbelow.
§Examples of address-sensitive types
§A self-referential struct
Self-referential structs are the simplest kind of address-sensitive type.
It is often useful for a struct to hold a pointer back into itself, whichallows the program to efficiently track subsections of the struct.Below, theslice field is a pointer into thedata field, whichwe could imagine being used to track a sliding window ofdata in parsercode.
As mentioned before, this pattern is also used extensively by compiler-generatedFutures.
usestd::pin::Pin;usestd::marker::PhantomPinned;usestd::ptr::NonNull;/// This is a self-referential struct because `self.slice` points into `self.data`.structUnmovable {/// Backing buffer.data: [u8;64],/// Points at `self.data` which we know is itself non-null. Raw pointer because we can't do /// this with a normal reference.slice: NonNull<[u8]>,/// Suppress `Unpin` so that this cannot be moved out of a `Pin` once constructed._pin: PhantomPinned,}implUnmovable {/// Creates a new `Unmovable`. /// /// To ensure the data doesn't move we place it on the heap behind a pinning Box. /// Note that the data is pinned, but the `Pin<Box<Self>>` which is pinning it can /// itself still be moved. This is important because it means we can return the pinning /// pointer from the function, which is itself a kind of move!fnnew() -> Pin<Box<Self>> {letres = Unmovable { data: [0;64],// We only create the pointer once the data is in place // otherwise it will have already moved before we even started.slice: NonNull::from(&[]), _pin: PhantomPinned, };// First we put the data in a box, which will be its final resting placeletmutboxed = Box::new(res);// Then we make the slice field point to the proper part of that boxed data. // From now on we need to make sure we don't move the boxed data.boxed.slice = NonNull::from(&boxed.data);// To do that, we pin the data in place by pointing to it with a pinning // (`Pin`-wrapped) pointer. // // `Box::into_pin` makes existing `Box` pin the data in-place without moving it, // so we can safely do this now *after* inserting the slice pointer above, but we have // to take care that we haven't performed any other semantic moves of `res` in between.letpin = Box::into_pin(boxed);// Now we can return the pinned (through a pinning Box) datapin }}letunmovable: Pin<Box<Unmovable>> = Unmovable::new();// The inner pointee `Unmovable` struct will now never be allowed to move.// Meanwhile, we are free to move the pointer around.letmutstill_unmoved = unmovable;assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data));// We cannot mutably dereference a `Pin<Ptr>` unless the pointee is `Unpin` or we use unsafe.// Since our type doesn't implement `Unpin`, this will fail to compile.// let mut new_unmoved = Unmovable::new();// std::mem::swap(&mut *still_unmoved, &mut *new_unmoved);§An intrusive, doubly-linked list
In an intrusive doubly-linked list, the collection itself does not own the memory in whicheach of its elements is stored. Instead, each client is free to allocate space for elements itadds to the list in whichever manner it likes, including on the stack! Elements can live on astack frame that lives shorter than the collection does provided the elements that live in agiven stack frame are removed from the list before going out of scope.
To make such an intrusive data structure work, every element stores pointers to its predecessorand successor within its own data, rather than having the list structure itself managing thosepointers. It is in this sense that the structure is “intrusive”: the details of how anelement is stored within the larger structure “intrudes” on the implementation of the elementtype itself!
The full implementation details of such a data structure are outside the scope of thisdocumentation, but we will discuss howPin can help to do so.
Using such an intrusive pattern, elements may only be added when they are pinned. If we thinkabout the consequences of adding non-pinned values to such a list, this becomes clear:
Moving or otherwise invalidating an element’s data would invalidate the pointers back to itwhich are stored in the elements ahead and behind it. Thus, in order to soundly dereferencethe pointers stored to the next and previous elements, we must satisfy the guarantee thatnothing has invalidated those pointers (which point to data that we do not own).
Moreover, theDrop implementation of each element must in some way notify itspredecessor and successor elements that it should be removed from the list before it is fullydestroyed, otherwise the pointers back to it would again become invalidated.
Crucially, this means we have to be able to rely ondrop always being called before anelement is invalidated. If an element could be deallocated or otherwise invalidated withoutcallingdrop, the pointers to it stored in its neighboring elements wouldbecome invalid, which would break the data structure.
Therefore, pinning data also comes withthe “Drop guarantee”.
§Subtle details and theDrop guarantee
The purpose of pinning is notjust to prevent a value from beingmoved, but moregenerally to be able to rely on the pinned valueremaining validat a specific place inmemory.
To do so, pinning a value adds anadditional invariant that must be upheld in order for useof the pinned data to be valid, on top of the ones that must be upheld for a non-pinned valueof the same type to be valid:
From the moment a value is pinned by constructing aPinning pointer to it, that valuemustremain,valid, at that same address in memory,until itsdrop handler iscalled.
There is some subtlety to this which we have not yet talked about in detail. The invariantdescribed above means that, yes,
- The value must not be moved out of its location in memory
but it also implies that,
- The memory location that stores the value must not get invalidated or otherwise repurposedduring the lifespan of the pinned value until its
dropreturns or panics
This point is subtle but required for intrusive data structures to be implemented soundly.
§Drop guarantee
There needs to be a way for a pinned value to notify any code that is relying on its pinnedstatus that it is about to be destroyed. In this way, the dependent code can remove thepinned value’s address from its data structures or otherwise change its behavior with theknowledge that it can no longer rely on that value existing at the location it was pinned to.
Thus, in any situation where we may want to overwrite a pinned value, that value’sdrop mustbe called beforehand (unless the pinned value implementsUnpin, in which case we can ignoreall ofPin’s guarantees, as usual).
The most common storage-reuse situations occur when a value on the stack is destroyed as partof a function return and when heap storage is freed. In both cases,drop gets run for usby Rust when using standard safe code. However, for manual heap allocations or otherwisecustom-allocated storage,unsafe code must make sure to callptr::drop_in_place beforedeallocating and re-using said storage.
In addition, storage “re-use”/invalidation can happen even if no storage is (de-)allocated.For example, if we had anOption which contained aSome(v) wherev is pinned, thenvwould be invalidated by setting that option toNone.
Similarly, if aVec was used to store pinned values andVec::set_len was used tomanually “kill” some elements of a vector, all of the items “killed” would become invalidated,which would beundefined behavior if those items were pinned.
Both of these cases are somewhat contrived, but it is crucial to remember thatPinned datamust bedropped before it is invalidated; not just to prevent memory leaks, but as amatter of soundness. As a corollary, the following code cannever be made safe:
// Pin something inside a `ManuallyDrop`. This is fine on its own.letmutpin: Pin<Box<ManuallyDrop<Type>>> = Box::pin(ManuallyDrop::new(Type));// However, creating a pinning mutable reference to the type *inside*// the `ManuallyDrop` is not!letinner: Pin<&mutType> =unsafe{ Pin::map_unchecked_mut(pin.as_mut(), |x|&mut **x)};Becausemem::ManuallyDrop inhibits the destructor ofType, it won’t get run when theBox<ManuallyDrop<Type>> is dropped, thus violating the drop guarantee of thePin<&mut Type>>.
Of course,leaking memory in such a way that its underlying storage will never get invalidatedor re-used is still fine:mem::forgeting aBox<T> prevents its storage from ever gettingre-used, so thedrop guarantee is still satisfied.
§Implementing an address-sensitive type.
This section goes into detail on important considerations for implementing your ownaddress-sensitive types, which are different from merely usingPin<Ptr> in a genericway.
§ImplementingDrop for types with address-sensitive states
Thedrop function takes&mut self, but this is calledeven if thatself has beenpinned! ImplementingDrop for a type with address-sensitive states requires some care, because ifself wasindeed in an address-sensitive state beforedrop was called, it is as if the compilerautomatically calledPin::get_unchecked_mut.
This can never cause a problem in purely safe code because creating a pinning pointer toa type which has an address-sensitive (thus does not implementUnpin) requiresunsafe,but it is important to note that choosing to take advantage of pinning-related guaranteesto justify validity in the implementation of your type has consequences for that type’sDrop implementation as well: if an element of your type could have been pinned,you must treatDrop as implicitly takingself:Pin<&mut Self>.
You should implementDrop as follows:
implDropforType {fndrop(&mutself) {// `new_unchecked` is okay because we know this value is never used // again after being dropped.inner_drop(unsafe{ Pin::new_unchecked(self)});fninner_drop(this: Pin<&mutType>) {// Actual drop code goes here.} }}The functioninner_drop has the signature thatdropshould have in this situation.This makes sure that you do not accidentally useself/this in a way that is in conflictwith pinning’s invariants.
Moreover, if your type is#[repr(packed)], the compiler will automaticallymove fields around to be able to drop them. It might even dothat for fields that happen to be sufficiently aligned. As a consequence, you cannot usepinning with a#[repr(packed)] type.
§ImplementingDrop for pointer types which will be used asPinning pointers
It should further be noted that creating a pinning pointer of some typePtralso carrieswith it implications on the way thatPtr type must implementDrop(as well asDeref andDerefMut)! When implementing a pointer type that may be used asa pinning pointer, you must also take the same care described above not tomove out of orotherwise invalidate the pointee duringDrop,Deref, orDerefMutimplementations.
§“Assigning” pinned data
Although in general it is not valid to swap data or assign through aPin<Ptr> for the samereason that reusing a pinned object’s memory is invalid, it is possible to do validly whenimplemented with special care for the needs of the exact data structure which is beingmodified. For example, the assigning function must know how to update all uses of the pinnedaddress (and any other invariants necessary to satisfy validity for that type). ForUnmovable (from the example above), we could write an assignment function like so:
implUnmovable {// Copies the contents of `src` into `self`, fixing up the self-pointer // in the process.fnassign(self: Pin<&mutSelf>, src: Pin<&mutSelf>) {unsafe{letunpinned_self = Pin::into_inner_unchecked(self);letunpinned_src = Pin::into_inner_unchecked(src);*unpinned_self =Self{ data: unpinned_src.data, slice: NonNull::from(&mut[]), _pin: PhantomPinned, };letdata_ptr = unpinned_src.data.as_ptr()as*constu8;letslice_ptr = unpinned_src.slice.as_ptr()as*constu8;letoffset = slice_ptr.offset_from(data_ptr)asusize;letlen = unpinned_src.slice.as_ptr().len(); unpinned_self.slice = NonNull::from(&mutunpinned_self.data[offset..offset+len]); } }}Even though we can’t have the compiler do the assignment for us, it’s possible to writesuch specialized functions for types that might need it.
Note that itis possible to assign generically through aPin<Ptr> by way ofPin::set().This does not violate any guarantees, since it will rundrop on the pointee value beforeassigning the new value. Thus, thedrop implementation still has a chance to perform thenecessary notifications to dependent values before the memory location of the original pinnedvalue is overwritten.
§Projections and Structural Pinning
With ordinary structs, it is natural that we want to addprojection methods that allowborrowing one or more of the inner fields of a struct when the caller has access to aborrow of the whole struct:
When working with address-sensitive types, it’s not obvious what the signature of thesefunctions should be. Iffield takesself:Pin<&mut Struct>, should itreturn&mut Field orPin<? This question also arises with&mut Field>enums and wrapper types likeVec<T>,Box<T>, andRefCell<T>. (This questionapplies just as well to shared references, but we’ll examine the more common case of mutablereferences for illustration)
It turns out that it’s up to the author ofStruct to decide which type the “projection”should produce. The choice must beconsistent though: if a pin is projected to a fieldin one place, then it should very likely not be exposed elsewhere without projecting thepin.
As the author of a data structure, you get to decide for each field whether pinning“propagates” to this field or not. Pinning that propagates is also called “structural”,because it follows the structure of the type.
This choice depends on what guarantees you need from the field for yourunsafe code to work.If the field is itself address-sensitive, or participates in the parent struct’s addresssensitivity, it will need to be structurally pinned.
A useful test is ifunsafe code that consumesPin<&mut Struct>also needs to take note of the address of the field itself, it may be evidence that that fieldis structurally pinned. Unfortunately, there are no hard-and-fast rules.
§Choosing pinningnot to be structural forfield…
While counter-intuitive, it’s often the easier choice: if you do not expose aPin<&mut Field>, you do not need to be careful about other codemoving out of that field, you just have to ensure is that you never create pinningreference to that field. This does of course also mean that if you decide a field does nothave structural pinning, you must not writeunsafe code that assumes (invalidly) that thefieldis structurally pinned!
Fields without structural pinning may have a projection method that turnsPin<&mut Struct> into&mut Field:
implStruct {fnfield(self: Pin<&mutSelf>) ->&mutField {// This is okay because `field` is never considered pinned, therefore we do not // need to uphold any pinning guarantees for this field in particular. Of course, // we must not elsewhere assume this field *is* pinned if we choose to expose // such a method!unsafe{&mutself.get_unchecked_mut().field } }}You may also in this situationimplUnpin for Struct {}even if the type offield is notUnpin. Since we have explicitly chosen not to care about pinning guaranteesforfield, the wayfield’s type interacts with pinning is no longer relevant in thecontext of its use inStruct.
§Choosing pinningto be structural forfield…
The other option is to decide that pinning is “structural” forfield,meaning that if the struct is pinned then so is the field.
This allows writing a projection that creates aPin<, thuswitnessing that the field is pinned:&mut Field>
implStruct {fnfield(self: Pin<&mutSelf>) -> Pin<&mutField> {// This is okay because `field` is pinned when `self` is.unsafe{self.map_unchecked_mut(|s|&muts.field) } }}Structural pinning comes with a few extra requirements:
Structural
Unpin. A struct can beUnpinonly if all of itsstructurally-pinned fields are, too. This isUnpin’s behavior by default.However, as a library author, it is your responsibility not to write something likeimpl<T>Unpin for Struct<T> {}and then offer a method that providesstructural pinning to an inner field ofT, which may not beUnpin! (Addinganyprojection operation requires unsafe code, so the fact thatUnpinis a safe trait doesnot break the principle that you only have to worry about any of this if you useunsafe)Pinned Destruction. As discussedabove,
droptakes&mut self, but the struct (and hence its fields) might have been pinnedbefore. The destructor must be written as if its argument wasself:Pin<, instead.&mut Self>As a consequence, the structmust not be
#[repr(packed)].Structural Notice of Destruction. You must uphold the
Dropguarantee: once your struct is pinned, the struct’s storage cannotbe re-used without calling the structurally-pinned fields’ destructors, as well.This can be tricky, as witnessed by
VecDeque<T>: the destructor ofVecDeque<T>can fail to calldropon all elements if one of the destructors panics. This violatestheDropguarantee, because it can lead to elements being deallocatedwithout their destructor being called.VecDeque<T>has no pinning projections, so its destructor is sound. If it wantedto provide such structural pinning, its destructor would need to abort the process if anyof the destructors panicked.You must not offer any other operations that could lead to data beingmoved out ofthe structural fields when your type is pinned. For example, if the struct contains an
Option<T>and there is atake-like operation with typefn(Pin<&mut Struct<T>>) ->,then that operation can be used to move aOption<T>Tout of a pinnedStruct<T>– whichmeans pinning cannot be structural for the field holding this data.For a more complex example of moving data out of a pinned type,imagine if
RefCell<T>had a methodfn get_pin_mut(self:Pin<.Then we could do the following:&mut Self>) ->Pin<&mut T>ⓘfnexploit_ref_cell<T>(rc: Pin<&mutRefCell<T>>) {// Here we get pinned access to the `T`.let _: Pin<&mutT> = rc.as_mut().get_pin_mut();// And here we have `&mut T` to the same data.letshared:&RefCell<T> = rc.into_ref().get_ref();letborrow = shared.borrow_mut();letcontent =&mut *borrow;}This is catastrophic: it means we can first pin the content of the
RefCell<T>(usingRefCell::get_pin_mut) and then move thatcontent using the mutable reference we got later.
§Structural Pinning examples
For a type likeVec<T>, both possibilities (structural pinning or not) makesense. AVec<T> with structural pinning could haveget_pin/get_pin_mutmethods to get pinning references to elements. However, it couldnot allow callingpop on a pinnedVec<T> because that would move the (structurallypinned) contents! Nor could it allowpush, which might reallocate and thus alsomove the contents.
AVec<T> without structural pinning couldimpl<T>Unpin for, because the contents are never pinnedand theVec<T>Vec<T> itself is fine with being moved as well.At that point pinning just has no effect on the vector at all.
In the standard library, pointer types generally do not have structural pinning,and thus they do not offer pinning projections. This is whyholds for allBox<T>:UnpinT. It makes sense to do this for pointer types, because moving theBox<T> does not actually move theT: theBox<T> can be freelymovable (akaUnpin) even if theT is not. In fact, evenPin< andBox<T>>Pin< are always&mut T>Unpin themselves, for the same reason:their contents (theT) are pinned, but the pointers themselves can be moved without movingthe pinned data. For bothBox<T> andPin<,whether the content is pinned is entirely independent of whether thepointer is pinned, meaning pinning isnot structural.Box<T>>
When implementing aFuture combinator, you will usually need structural pinningfor the nested futures, as you need to get pinning (Pin-wrapped) references to them tocallpoll. But if your combinator contains any other data that does not need to be pinned,you can make those fields not structural and hence freely access them with amutable reference even when you just havePin<(such as in your own&mut Self>poll implementation).
Futures themselves do not ever need to notify other bits of code thatthey are being dropped, however data structures like stack-based intrusive linked lists do. ↩
There is a bit of nuance here that is still being decided about what the aliasingsemantics of
Pin<&mut T>should be, but this is true as of today. ↩
Macros§
Structs§
- Pin
- A pointer which pins its pointee in place.
- Unsafe
Pinned Experimental - This type provides a way to entirely opt-out of typical aliasing rules;specifically,
&mut UnsafePinned<T>is not guaranteed to be a unique pointer.This also subsumes the effects ofUnsafeCell, i.e.,&UnsafePinned<T>may point to datathat is being mutated.
Traits§
- PinCoerce
Unsized Experimental - Trait that indicates that this is a pointer or a wrapper for one, whereunsizing can be performed on the pointee when it is pinned.