Closed withoutboats closed 5 years ago
To bikeshed a bit more, today at lunch I came up with LeavePin
. It carries the same tone as escape
without the implied misleading semantics.
Are there any interesting interactions between impl specialistion and pins, as is with lifetimes?
The substring "specializ" occurs in tracking issue discussion a bit, but it's not conclusive. Pin RFC or specialisation RFC should explicitly mention this interaction, either as "It's verified to be OK" or "further research is needed to judge it safe".
@vi not only is there no soundness interaction, its not possible for there to be a soundness interaction. The pin APIs are strictly defined in the standard library and involve no new language features, a user can define them just as easily in a third party libraries. If a language feature is unsound in the face of this library code existing, it is unsound period, because any user could write this library code today and it will compile fine.
If a language feature is unsound in the face of this library code existing, it is unsound period, because any user could write this library code today and it will compile fine.
Not that it should have any bearing on pin
... but I don't think it's nearly as simple as that.
If a library feature, when using unsafe
, is making use language constructs whose behavior are as of yet unspecified (e.g. &packed.field as *const _
, or making various assumptions about ABI) then if additional language changes invalidate the assumptions of those libraries then I think that it is the libraries which are unsound and not the language changes. On the other hand, if language changes make defined behavior unsound then it's the fault of the language changes. Compiling fine is thus not a sufficient condition for the soundness of a library in the face of unsafe and language changes.
+1 to MoveFromPin or similar
If you ask the question "When should I unimplement Unpin for my own type?", the answer is much more clear if you instead ask "When should I unimplement MoveFromPin for my own type?"
Same with "Should I add Unpin as a trait bound here?" vs "should I add MoveFromPin as a trait bound here?"
Pinning is not !Move
Sorry if this was mentioned somewhere, but I only skimmed the vast amount of discussion there has been around Pin here, in the implementation issue, and in the RFC issue.
Will rust ever have !Move? I can certainly see use cases for such a thing (heck, I came looking about Pin because I was looking for a way not to shoot myself in the foot with types that can't be moved). If the answer is yes, how would that interact with Pin? Would the existence of Pin make it harder than it already is to add !Move?
The final comment period, with a disposition to merge, as per the review above, is now complete.
There should be an unresolved concern around the naming of Unpin.
As @RalfJung pointed out, #55992 also only adds a small amount of the extra documentation asked for in https://github.com/rust-lang/rust/issues/55766#issuecomment-438316891 and elsewhere. Don't know that that's grounds for not merging yet though.
Why does my drop() method now again take &mut self, instead of a Pin.
Well, drop() is old -- it exists since Rust 1.0 -- so we cannot change it. We'd love to just make it take Pin<&mut Self>, and then Unpin types could get their &mut like they do now, but that's a non-backwards-compatible change.
I was wondering if it would be possible to implement this change in a backward compatible way. AIUI until we add Unpin
(and people can specify !Unpin
) all types implement Unpin
. So we could add a trait:
trait DropPinned {
fn drop(Pin<&mut> self);
}
and then impl this trait for all Unpin
types, which until people can opt out is all types. Something like:
impl<T> PinDrop for T where T:Unpin + Drop {
fn drop(Pin<&mut T> self) {
Drop::drop(self.get_mut());
}
}
Then we'd have the compiler insert calls to DropPinned::drop
instead of Drop::drop
. Essentially trait DropPinned
becomes the lang-item rather than Drop
. AFAICS this would be backward compatible if and only if this mechanism was introduced at the same time as Unpin
.
There should be an unresolved concern around the naming of Unpin.
@tikue No libs team member filed a concern with rfcbot before or during the FCP, and I don't believe any of the arguments about Unpin
were new or novel to this thread, so typically our process would consider the current naming to be finalized. Obviously if someone has concerns they should speak up, but the point of having team checkboxes followed by FCP is to ensure that the everyone is comfortable stabilizing the API as-proposed.
@cramertj I'm a little confused. Several people have spoken up. When I asked for references about where else the arguments about the naming of Unpin
had been raised and resolved, I was pointed to https://internals.rust-lang.org/t/naming-pin-anchor-move/6864, which as far as I can see also has people complaining about the naming of Unpin
, and no real counter-argument. The original RFC in https://github.com/rust-lang/rfcs/pull/2349 also does not have much rationale as to why proposed alternatives to Unpin
are worse. Even in this thread, it seems like the only counter-arguments that really come up are "it's shorter" and "it's technically correct". Are you able to point to concrete discussion where alternative names that are easier to understand (such as MoveFromPin
) are discussed and rejected?
I have explained in previous comments why I believe novel perspectives have been brought up in this thread. I've been following the pin API discussions fairly closely since inception and don't remember ever seeing the double negative issue brought up before this thread.
@tikue I've brought up and seen the double-negative issue brought up multiple times, and the precise naming of Unpin
has been raised as an issue many times over and has consistently been resolved in favor of Unpin
. As I said before, if someone on the libs team wants to register a (late) objection then I'm fine responding to that, but they all signed off on the stabilization proposal above which clearly does not include the naming of Unpin
as an unresolved question: we've discussed the alternatives, and this FCP was the process for deciding that we were prepared to stabilize on the decisions that had been made, including the name Unpin
.
@cramertj Could you please provide a link to where that discussion has happened? I'm not doubting you, would just like to see the arguments in favor of Unpin
, because I do not believe they have been given here. As mentioned, the references I've been given so far do not provide any resolution on the naming of Unpin
.
@cramertj +1 to @jonhoo's ask. If there are discussions that the libs team had among themselves that are not registered in official channels, I think the main thrust of those discussions ought to be reiterated here. I think there is even an official rule that RFC decisions can only be made based on publicly-known arguments?
I think there is even an official rule that RFC decisions can only be made based on publicly-known arguments?
Yup, that's true-- and for the record, I'm not on the libs team and so haven't been present for any libs-team only discussions. Looking through the RFC thread and the Pin
tracking issue, there were numerous times where the name of Unpin
was brought up. I don't see anyone specifically say the words "double negative", but I certainly remember "!Unpin
is a double negative" being brought up before, in addition to the general API rule of naming traits for what you can do with them, rather than what you can't (as I pointed out above, I think Unpin
actually follows both these rules, though realizing that requires hearing "Unpin" as a verb rather than hearing it as an adjective "not-pin", which isn't intuitive to folks).
@wmanley That does not work, e.g. impl<T> Drop for Vec<T>
would break because we do not have Vec<T>: Unpin
. Also, proposals along this line have been made before, even in this thread. Please read the discussions before replying. I know that's asking quite a lot, but it is the only way to avoid the same issues being explained again and again.
I think there is even an official rule that RFC decisions can only be made based on publicly-known arguments?
This is informally known as the "no new rationale" rule.
Not sure where to post this, but could someone take a look at https://github.com/rust-lang/rust/issues/56256 ?
Related to #56256, impl<T> From<Box<T>> for Pin<Box<T>>
is not listed in the OP as being stabilized, but it will implicitly become stable once Pin
does. Are there any other trait implementations that are non-trivial and should be looked at for stabilization? (scanning the docs all the others appear to be trivial delegating implementations for the wrapped pointer to me).
We talked about this Issue in the libs team today. The last weeks of discussion have shown that there are still a few things that should get addressed first, especially regarding the naming of Unpin
.
Therefore we will not move forward with stabilizing this for now (FCP notwithstanding).
It would be much appreciated if someone could gather up the ideas in this thread and prepare a standalone proposal for improving the naming situation.
@Kimundi
It would be much appreciated if someone could gather up the ideas in this thread and prepare a standalone proposal for improving the naming situation.
Does this mean that the libs team is unwilling to stabilize the current APIs as-is? I personally don't think that anyone has come up with a better name than the current set of names as-implemented, and so I'm not able to make such a proposal, but I do care a lot about seeing these APIs stabilized, so if someone from the libs team has a name they'd prefer then :shipit:
Bikeshedded this with a bunch of coworkers and @anp suggested DePin
, which I actually like quite a bit, since it removes the "not pin" connotation of Unpin
and emphasizes that it's talking about a type which can be de-Pin
'd.
@Kimundi can you or someone from the libs team please register an explicit "fcp concern" to take this out of FCP and lay out more clearly what is meant by "a few things taht should get addressed"?
@rfcbot concern naming-of-Unpin
I'm not sure if this works really once FCP is entered, but I'd like to place a formal blocking concern on the naming of the Unpin
trait. The Unpin
trait looks to be pretty crucial to the API, and the "double negative" (as discussed above) throws me for a spin every time I read it.
There's been a whole bunch of comments about various names, but unfortunately I'm not overly thrilled with any yet. My "favorite" is still along the lines of MoveFromPin
or Relocate
(my god there are so many comments here I have no idea how to review this).
I'm personally ok with the naming of Pin
itself as well as Pinned
for a ZST that doesn't implement the Unpin
trait.
I completely agree with @alexcrichton that the naming of Unpin
is the big point of contention here. I don't think there are any technical concerns as far as I can see about the proposed feature itself (though there are a lot of comments, so could have missed something).
I do still think Pinned
is a weird name for the ZST, because something that contains a Pinned
isn't really pinned.. It just isn't Unpin
. PhantomPinned
(as it's been renamed to in #55992) has the same issue in that it refers to Pin
, when the ZST is really about Unpin
.
I also still think the docs need more work given the subtlety of this feature, but that probably doesn't count as a blocker.
Also, @Kimundi, I'm happy to see that the libs team is willing to give this a little more time to settle. It may seem a lot like unnecessary bikeshedding, but I (and others too it seems) think it is pretty important to improve the teachability of this feature :)
Agreed with @jonhoo about Pinned
still feeling weird, and PhantomPinned
not being any better (it's not Pinned in any way, not even in a phantom way). I think if we find a good name for Unpin
, then Pinned
will naturally lend itself to being renamed Not{NewNameForUnpin}
.
I really don't think we need to spend any more time discussing PhantomPinned
-- this type will almost never appear to end users. PhantomNotUnpin
/PhantomNotDePin
/PhantomNotMoveFromPin
/ etc. aren't going to make it any more or less common or make the API more or less obvious to users who are already comfortable enough with the API to have come up with a legitimate use for PhantomPinned
.
Just a quick idea: trait Move
and ZST Anchor
.
Every type is Move
able, unless it contains an Anchor
that makes it stick to a Pin
.
I like how Anchor: !Move
intuitively makes a lot of sense.
I'm not suggesting we spend time specifically on PhantomPinned
, but I do think it'd be low effort to keep an open mind, because it's possible that whatever is landed on for Unpin
will work just as well for PhantomPinned
.
We've covered Move
and explained why it's inappropriate numerous times. All types are movable until they are pinned. Similarly, Anchor
has previously been suggested but it isn't clear at all from the name that it is used to opt out of Unpin
ing/Move
ing.
@stjepang I think Move
has been discarded a while ago because something being !Unpin
does not actually prevent it from being moved. It's only if a type is under a Pin
, and it isn't Unpin
, that you are "contractually obliged" not to move it.
In the original comment @withoutboats said:
The
Pin
wrapper modifies the pointer to "pin" the memory it refers to in place
I think it's odd that types implement Unpin
because values are not "pinned" - memory is. However, it makes sense to talk about values that are "safe to move". What if we rename Unpin
to MoveSafe
?
Consider the UnwindSafe
trait. It's just a "hinting" marker trait and is safe to implement. If you let a !UnwindSafe
value cross a catch_unwind
boundary (with AssertUnwindSafe
), you'll "break" it by invalidating its invariants.
Similarly, if you have a !Unpin
/!MoveSafe
value, it can still be moved (of course), but you'll "break" it by invalidating its self-references. The concept seems similar.
The trait Unpin
really just means MoveSafe
. It seems to me it's not about values that can be moved out of memory behind Pin
. Rather, it's about values that won't "break" when you move them.
MoveSafe
has the same problem as Move
-- all values any type can be safely moved. It only becomes impossible to move a value after it has been pinned.
MoveSafe
has the same problem asMove
-- all values any type can be safely moved.
Right, but it's a different meaning of "safe", just like in UnwindSafe
. Anyways, I'd be fine with Relocate
or anything of that sort.
To summarize, I feel that the trait name should not be DePin
, Unpin
, or anything with "pin" in its name. To me, that's the main source of confusion. The trait is not really an "escape hatch" from the shackles of Pin
- the trait says the value won't be invalidated when moved.
I just see Pin
and Unpin
as totally separate things. :)
I feel the exact opposite ;). The trait is only meaningful with respect to Pin, which is the only type we have which meaningfully expresses constraints on the movability of the underlying value. Without Pin, Unpin is entirely meaningless.
I like to think about pinning as putting something on a pinboard. If you remove the pin of an object that is pinned to the board, you can move the object. Removing the pin is Unpinning.
I like the name Unpin
.
I can also see how !Unpin is a double negation, and can cause confusion. However, I wonder how often you need to write !Unpin
.
One other name I could come up with for Unpin is Detach
. Back to the pinboard metaphor, you wouldn't Unpin, but Detach the Pin from its object.
I think I really like DePin
! So far it's my favorite -- it's concise, it's clearly a verb and not an adjective, and !DePin
seems pretty clear as well ("cannot be de-pinned").
I think it's odd that types implement Unpin because values are not "pinned" - memory is.
The value is pinned to memory. But the value is crucially important here as well. Pinning memory, for me, is just about making sure that it remains dereferencable or so, but it is not violated by mem::swap
. Pinning a value to memory is about not moving that pinned value anywhere else, which is exactly what Pin
is about.
I can also see how !Unpin is a double negation, and can cause confusion. However, I wonder how often you need to write !Unpin.
I hear you when you say you don't find the name confusing. I, and many many others in this thread have found ourselves confused by the naming.
I don't have the code at hand, but the first time I tried to use futures I wanted a function to return an impl Future<Output=T>
. I can't remember what happened, but I immediately got a gnarly compiler error complaining about T and Unpin. The question I needed an answer for was "Is it safe to constrain T to only be Unpin". And that led me to stare into the abyss for about 2 harrowing hours.
"Oh, ok so if thats what Pin means. So Unpin means .. Box? Why is that a trait?"
"Wait, impl !Unpin
? Why is that not impl Pin
?"
"Right, Pin and Unpin ... aren't opposites. They're totally different things. Wait, then what does Unpin mean again? Why is it called that?"
"What on earth does !Unpin
mean? Not ... the other, not pin thing? Hrrrgh"
Still the only way this makes sense in my head is by replacing 'unpin' with 'relocatable'. "Type is not relocatable in memory" makes complete sense. And yet, even knowing what Pin does, "type is not unpin" leaves me confused.
Renaming Unpin
to Relocatable
(or Relocate
) gets my vote 🗳. But I'd find just about any of the other suggestions better than Unpin.
The trait is only meaningful with respect to Pin, which is the only type we have which meaningfully expresses constraints on the movability of the underlying value. Without Pin, Unpin is entirely meaningless.
To put a point on this - the guarantees around pin are entirely around certain behaviors, not types. For example, a generator function which yields ()
could trivially implement FnOnce
by resuming repeatedly. While this type might not implement Unpin
- because its yield states could be self-referential, its FnOnce
interface (which moves itself) is completely safe because it hasnt yet been pinned when you FnOnce
it, which is necessary to bring it into a self-referential state. Unpin
is specifically about the kinds of behaviors that are asserted to be safe once the type has been pinned (namely, moving it), not about some intrinsic property of the type.
Ironically enough I just came here to comment that while the Unpin
naming definitely has been debated in the past, the debate over it I remember witnessing was when we chose to replace Move
with Unpin
, which, I was going to say, is an unambiguous improvement. And often one only realizes later on that, while the current design is a definite improvement over what came before, it still has some room left for yet further improvement. Which is the direction I was coming at this from.
Unpin is specifically about the kinds of behaviors that are asserted to be safe once the type has been pinned (namely, moving it), not about some intrinsic property of the type.
The behavior when pinned is an intrinsic property of the type, just like the behavior/invariant when it is shared. I went into this in a lot more detail in this blog post.
@RalfJung we're speaking past each other. There's a confusion I see a lot that self-referential types "cannot be moved" - this is not accurate, they have certain states they can enter into during which they cannot be moved, but while they are in other states, it is perfectly safe to move them (and our APIs rely on the ability to move them, for example to apply combinators to them or to move them into a Pin<Box<>>
). I'm trying to clarify that its not the case that these types "cannot be moved."
Ah, yes, then I agree. A !DePin
type is not always pinned, and when it is not, it can be moved like any other type.
@cramertj @withoutboats one thing I haven't quite been able to surise yet, are y'all against renaming Unpin
? It doesn't sound like y'all feel you agree it needs to be renamed, but I'm not sure if you're against it.
I personally don't think the main problem here lies with the name Unpin
and that "if we could just rename it everything would be intuitive". While renaming may help a little bit (and Relocate / DePin seem nice here...), I think the main complexity comes from the concept of pinning itself; it's certainly not one of the easiest concepts to understand or explain; not even nearly.
Therefore, I think the documentation of the core::pin
module needs to be beefed up significantly and a lot more examples need to be included. Good examples would both show canonical use cases as well as uses of the unsafe functions and implementations which are unsound.
@alexcrichton I'm not against renaming, no. I think Unpin
is already fine, but I'd be okay with DePin
, which is why I suggested it. RemoveFromPin
is the most obviously "correct", but verbose to the point of syntactic salt, so I think I'd oppose that name specifically. I am, however, opposed to putting off stabilization indefinitely until we find a name that everyone agrees is the best-- I think the API has some inherent complexities that won't be made dramatically better or worse because of the name of the Unpin
trait. I'd really like to push towards stabilization soon in order to prevent more churn to the code, docs, and discussions around futures_api
(and so we can start getting the pieces in place to stabilize futures_api
itself). Perhaps we should schedule a dedicated name-settling VC meeting so that everyone who has an opinion can pitch their solution and we can have a more high-bandwidth opportunity to settle this?
My vote is std::pin::Reloc
(Relocate)
I have two very hypothetical situations (well, really just one, but two different executions) of which I wouldn't mind having explicitly stated wether it is allowed or not.
Unpin
indicates that it is trivial to transition from all of T
's (where T: Unpin
) states while in the pinned type-state (I hope I am correctly remembering that term from one of @RalfJung's blog posts) to the not-pinned type-state. Which is why Pin
can hand out &mut T
.
So let's assume I want to create a type Woof
for which it is also always possible to transition from all of Woof
's states while in the pinned type-state to the not-pinned type-state, but it isn't trivial to do so and can therefor not implement Unpin
, would it be allowed for Woof
to have a function like fn unpin(self: Pin<Box<Woof>>) -> Box<Woof>
?
Same for a type Meow
for which it is only sometimes possible to transition from pinned to not-pinned and a function like fn unpin(self: Pin<Box<Meow>>) -> Result<Box<Meow>, Pin<Box<Meow>>>
.
@rfcbot fcp merge Feature name:
pin
Stabilization target: 1.32.0 Tracking issue: #49150 Related RFCs: rust-lang/rfcs#2349This is a proposal to stabilize the
pin
library feature, making the "pinning" APIs for manipulating pinned memory usable on stable.(I've tried to write this proposal as a comprehensive "stabilization report.")
Stabilized feature or APIs
[std|core]::pin::Pin
This stabilizes the
Pin
type in thepin
submodule ofstd
/core
.Pin
is a fundamental, transparent wrapper around a generic typeP
, which is intended to be a pointer type (for example,Pin<&mut T>
andPin<Box<T>>
are both valid, intended constructs). ThePin
wrapper modifies the pointer to "pin" the memory it refers to in place, preventing the user from moving objects out of that memory.The usual way to use the
Pin
type is to construct a pinned variant of some kind of owning pointer (Box
,Rc
, etc). The std library owning pointers all provide apinned
constructor which returns this. Then, to manipulate the value inside, all of these pointers provide a way to degrade towardPin<&T>
andPin<&mut T>
. Pinned pointers can deref, giving you back&T
, but cannot safely mutably deref: this is only possible using the unsafeget_mut
function.As a result, anyone mutating data through a pin will be required to uphold the invariant that they never move out of that data. This allows other code to safely assume that the data is never moved, allowing it to contain (for example) self references.
The
Pin
type will have these stabilized APIs:impl<P> Pin<P> where P: Deref, P::Target: Unpin
fn new(pointer: P) -> Pin<P>
impl<P> Pin<P> where P: Deref
unsafe fn new_unchecked(pointer: P) -> Pin<P>
fn as_ref(&self) -> Pin<&P::Target>
impl<P> Pin<P> where P: DerefMut
fn as_mut(&mut self) -> Pin<&mut P::Target>
fn set(&mut self, P::Target);
impl<'a, T: ?Sized> Pin<&'a T>
unsafe fn map_unchecked<U, F: FnOnce(&T) -> &U>(self, f: F) -> Pin<&'a U>
fn get_ref(self) -> &'a T
impl<'a, T: ?Sized> Pin<&'a mut T>
fn into_ref(self) -> Pin<&'a T>
unsafe fn get_unchecked_mut(self) -> &'a mut T
unsafe fn map_unchecked_mut<U, F: FnOnce(&mut T) -> &mut U>(self, f: F) -> Pin<&'a mut U>
impl<'a, T: ?Sized> Pin<&'a mut T> where T: Unpin
fn get_mut(self) -> &'a mut T
Trait impls
Most of the trait impls on
Pin
are fairly rote, these two are important to its operation:impl<P: Deref> Deref for Pin<P> { type Target = P::Target }
impl<P: DerefMut> DerefMut for Pin<P> where P::Target: Unpin { }
std::marker::Unpin
Unpin is a safe auto trait which opts out of the guarantees of pinning. If the target of a pinned pointer implements
Unpin
, it is safe to mutably dereference to it.Unpin
types do not have any guarantees that they will not be moved out of aPin
.This makes it as ergonomic to deal with a pinned reference to something that does not contain self-references as it would be to deal with a non-pinned reference. The guarantees of
Pin
only matter for special case types like self-referential structures: those types do not implementUnpin
, so they have the restrictions of thePin
type.Notable implementations of
Unpin
in std:impl<'a, T: ?Sized> Unpin for &'a T
impl<'a, T: ?Sized> Unpin for &'a mut T
impl<T: ?Sized> Unpin for Box<T>
impl<T: ?Sized> Unpin for Rc<T>
impl<T: ?Sized> Unpin for Arc<T>
These codify the notion that pinnedness is not transitive across pointers. That is, a
Pin<&T>
only pins the actual memory block represented byT
in a place. Users have occassionally been confused by this and expected that a type likePin<&mut Box<T>>
pins the data ofT
in place, but it only pins the memory the pinned reference actually refers to: in this case, theBox
's representation, which a pointer into the heap.std::marker::Pinned
The
Pinned
type is a ZST which does not implementUnpin
; it allows you to supress the auto-implementation ofUnpin
on stable, where!Unpin
impls would not be stable yet.Smart pointer constructors
Constructors are added to the std smart pointers to create pinned references:
Box::pinned(data: T) -> Pin<Box<T>>
Rc::pinned(data: T) -> Pin<Rc<T>>
Arc::pinned(data: T) -> Pin<Arc<T>>
Notes on pinning & safety
Over the last 9 months the pinning APIs have gone through several iterations as we have investigated their expressive power and also the soundness of their guarantees. I would now say confidently that the pinning APIs stabilized here are sound and close enough to the local maxima in ergonomics and expressiveness; that is, ready for stabilization.
One of the trickier issues of pinning is determining when it is safe to perform a pin projection: that is, to go from a
Pin<P<Target = Foo>>
to aPin<P<Target = Bar>>
, whereBar
is a field ofFoo
. Fortunately, we have been able to codify a set of rules which can help users determine if such a projection is safe:(Foo: Unpin) implies (Bar: Unpin)
: that is, if it is never the case thatFoo
(the containing type) isUnpin
whileBar
(the projected type) is notUnpin
.Bar
is never moved during the destruction ofFoo
, meaning that eitherFoo
has no destructor, or the destructor is carefully checked to make sure that it never moves out of the field being projected to.Foo
(the containing type) is notrepr(packed)
, because this causes fields to be moved around to realign them.Additionally, the std APIs provide no safe way to pin objects to the stack. This is because there is no way to implement that safely using a function API. However, users can unsafely pin things to the stack by guaranteeing that they never move the object again after creating the pinned reference.
The
pin-utils
crate on crates.io contains macros to assist with both stack pinning and pin projection. The stack pinning macro safely pins objects to the stack using a trick involving shadowing, whereas a macro for projection exists which is unsafe, but avoids you having to write the projection boilerplate in which you could possibly introduce other incorrect unsafe code.Implementation changes prior to stabilization
Unpin
from the prelude, removepin::Unpin
re-exportAs a general rule, we don't re-export things from multiple places in std unless one is a supermodule of the real definition (e.g. shortening
std::collections::hash_map::HashMap
tostd::collections::HashMap
). For this reason, the re-export ofstd::marker::Unpin
fromstd::pin::Unpin
is out of place.At the same time, other important marker traits like Send and Sync are included in the prelude. So instead of re-exporting
Unpin
from thepin
module, by putting in the prelude we make it unnecessary to importstd::marker::Unpin
, the same reason it was put intopin
.Currently, a lot of the associated function of
Pin
do not use method syntax. In theory, this is to avoid conflicting with derefable inner methods. However, this rule has not been applied consistently, and in our experience has mostly just made things more inconvenient. Pinned pointers only implement immutable deref, not mutable deref or deref by value, limiting the ability to deref anyway. Moreover, many of these names are fairly unique (e.g.map_unchecked
) and unlikely to conflict.Instead, we prefer to give the
Pin
methods their due precedence; users who need to access an interior method always can using UFCS, just as they would be required to to access the Pin methods if we did not use method syntax.get_mut_unchecked
toget_unchecked_mut
The current ordering is inconsistent with other uses in the standard library.
impl<P> Unpin for Pin<P>
This impl is not justified by our standard justification for unpin impls: there is no pointer direction between
Pin<P>
andP
. Its usefulness is covered by the impls for pointers themselves.This futures impl will need to change to add a
P: Unpin
bound.Pin
asrepr(transparent)
Pin should be a transparent wrapper around the pointer inside of it, with the same representation.
Connected features and larger milestones
The pin APIs are important to safely manipulating sections of memory which can be guaranteed not to be moved out. If the objects in that memory do not implement
Unpin
, their address will never change. This is necessary for creating self-referential generators and asynchronous functions. As a result, thePin
type appears in the standard libraryfuture
APIs and will soon appear in the APIs for generators as well (#55704).Stabilizing the
Pin
type and its APIs is a necessary precursor to stabilizing theFuture
APIs, which is itself a necessary precursor to stabilizing theasync/await
syntax and moving the entirefutures 0.3
async IO ecosystem onto stable Rust.cc @cramertj @RalfJung