Is there any secret implementation that do the magic and let the Boxed Pin to safely update the refrences?
I'm confused sorry
Because moving a Box<T>
does not change where the T
is stored. This isn't magic â it's just how heap allocations work.
@alice, no, the fact that Box<T>
implements Unpin
unconditionally has got nothing to do with the fact that Box
introduces an indirection. You can build a wrapper without any indirection such that pinning is not structural for the field, e.g. with pin-project
use pin_project::pin_project;
#[pin_project]
struct UnpinWrapper<T>(T);
fn assert_unpin<T: Unpin>() {}
fn test<T>() {
// this compiles because UnpinWrapper<T>: Unpin
// is true even if T: !Unpin
assert_unpin::<UnpinWrapper<T>>();
}
Edit: Well, actually, Unpin
is a safe trait, so that wrapper doesnât need pin-project
. That crate with its safe macros is only really needed if you do want some fields to do structural pinning.
struct UnpinWrapper<T>(T);
impl<T> Unpin for UnpinWrapper<T> {}
Okay, maybe you can be a bit more precise in the wording. Anything can be Unpin
if it wants to. In the case of Box
, it is Unpin
because it makes sense for heap allocated structures to be unconditionally Unpin
.
To understand pinning better try reading the module-docs of std::pin and the API of Pin
. The important things that T: Unpin
allows is
-
converting
Pin<SomePointerType<T>
intoSomePointerType<T>
viaPin::into_inner
- e.g.
Pin<&T>
to&T
orPin<&mut T>
to&mut T
(this conversion is also provided byPin::get_mut
)
orPin<Box<T>>
toBox<T>
- e.g.
-
freely creating
Pin<SomePointerType<T>>
without needing to hold up any guarantees, by usingPin::new
For implementing T: Unpin
soundly, the thing to keep in mind is that you must not also implement some kind of Pin<&mut T>
-> Pin<&mut SomeField>
projection. The reason why Unpin
implementations can be done without unsafe
is that these projections do require use of unsafe
except if youâre using macros such as the one provided by pin-project
.
What alice said is true, but it doesnât have anything to do with the Unpin for Box<T>
implementation.
Because moving a
Box<T>
does not change where theT
is stored.
This is in fact the reason why Box::pin
exists and lets you turn T
into Pin<Box<T>>
for any type T
. Afterwards, the Pin<Box<T>>
can be moved freely while also providing mutable pinned views Pin<&mut T>
via Pin::as_mut
.
A very simple but important point!
Thanks
The point is unlike stack movement, heap moves do not need to copy the memory. They just swap the ownerships
Every move and copy are guaranteed to be equivalent to the single memcpy call, if not optimized out. There's no exceptions, since it's the language built-in rule instead of some conventions like C++ "move semantics". Not any user defined code like swapping can run in this process. It's just moving Box<T>
doesn't copy the T
. It only copy the ptr.
Note that the move and the copy are strictly identical on runtime operation. They only differ at compile time, specifically using moved out value throws compile error while using copied out value is valid.
IOW, it has nothing to do with "stack vs. heap". It has to do with "value vs. pointer".
This seems like a good example of read the manual/docs :
Where a structural projection from Box<T>
to T
would be a non-unsafe
function such as:
fn pin_project_box<T> (p: Pin<&mut Box<T>>) -> Pin<&mut T>
If Box
where to feature such a function, then indeed we'd need a T : Unpin
bound on the impl of Unpin
for Box<T>
.
And, indeed, the motivation for Box
and most pointer types not to offer such a "structural" Pin
-projection, is that the Box<T>
, in and of itself, does not live in the same storage / location as the T
it points to. In other words, types such as Pin<&mut Box<T>>
are "silly" to begin with.
@steffahn sorry I have miss-clicked my answer, I did not inted to direct my post to you specifically â quite the contrary I may add!
Since youâre answering to my comment, Iâll answer back:
I myself are very aware of those docs, I even reformatted the whole thing recently and also linked them in a post above
and â by the way â clicking on the word âstructuralâ
takes you to the docs as well, directly to the relevant section.
Nonetheless I appreciate your effort in quoting and further explaining (for others) some relevant part directly in this thread. I personally think that the documented motivation section on why Box
doesnât do structural pinning feels somewhat somewhat weak. It really boils down to âwe donât want to offer pin projections for Box
now or in the futureâ and a type that never implements those might as well implement Unpin
then. The Unpin
implementation then probably also leads to some useful things like the fact that e.g. Pin<Box<dyn Future<Output = T>>>
can implement Future
itself, too.
That came slightly too late
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.