Just wanted to leave a pointer to my proposal to remove Pin
entirely in favor of a library-only variant of !Move
. Pin<T>
would become &mut T
, and &T
would also be available, so we could have immutable pinned references (plus &move
in the future) without an explosion of new types, and without the poor ergonomics of, e.g., needing an explicit method call to convert PinMut<T>
to Pin<T>
.
Despite what you might expect, this approach would be compatible with the extra invariant proposed by @RalfJung. Basically, my proposal still includes PinBox
, but with a different approach for it to provide the no-move guarantee:
trait Anchor {
type Inner: ?Sized;
// PinBox and friends would use this method to implement DerefMut;
// it's unsafe because you should only call it if you can guarantee
// that `self` will never move again.
unsafe fn get_mut(&mut self) -> &mut Self::Inner;
}
Adding the invariant would ‘just’ be a change to the contract of Anchor
to also have callers of get_mut
(such as PinBox
) ensure that the Anchor
is dropped before its memory is reused.
As an illustration, to implement Entry
under this scheme, just like any other immovable struct, you’d use two types – one to hold the actual data, and one that’s just a marker:
struct EntryAnchor<T> {
x: T,
objects: Cell<whatever>,
}
extern { type Entry<T>; }
// Boilerplate
impl<T> Anchor for EntryAnchor<T> {
type Inner = Entry<T>;
unsafe fn get_mut(&mut self) -> &mut Entry<T> {
&mut *(self as *mut EntryAnchor<T> as *mut Entry<T>)
}
}
And all methods using Entry
would do the reverse cast from &mut Entry<T>
to &mut EntryAnchor<T>
(or &Entry<T>
to &EntryAnchor<T>
).
Note that the two-struct requirement is essentially just a stopgap until immovable structs are eventually added as a builtin-language feature. In the meantime, of course, we could use a macro to reduce boilerplate.
By the way, suppose we want to allow T
to also be immovable. This requires a bit of extra work in both @RalfJung’s version and mine, but not too much. In his version, there would need to be an (unsafely-implemented) custom accessor method to go from Pin<Entry<T>>
to Pin<T>
. In mine, the definition would be changed to
struct EntryAnchor<A: Anchor> {
x: A,
objects: Cell<whatever>,
}
and the accessor method would look like:
impl<A: Anchor> Entry<A> {
fn get(&mut self) -> &mut <A as Anchor>::Inner {
// usual conversion that applies to every method:
let anchor = &mut (self as *mut Entry<A> as *mut EntryAnchor<A>);
// the actual work is just:
unsafe { anchor.x.get_mut() }
}
}
(edit: fixed above example)
…but doesn’t that make things unergonomic if the contained type doesn’t need to be immovable, since it’d force us to unnecessarily define an Anchor
type? No, because I’m envisioning there would be a blanket impl of Anchor
for non-immovable (i.e. Sized
) types:
impl<T> Anchor for T {
type Inner = Self;
unsafe fn get_mut(&mut self) -> &mut Self { self }
}
so you could use EntryAnchor<MyStruct>
, and it would just work without needing a separate MyStructAnchor
.