Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2021: Move Constructors by Miguel Young de la Sota

RustConf
September 14, 2021

2021: Move Constructors by Miguel Young de la Sota

A "self-referential" type is one that holds a reference into itself; async Futures are the most common self-referential types in Rust today. However, they can't be moved without invalidating the reference, so they're pinned on the heap or stack. You can't return them or put them into collections (without Boxing, of course). C++ makes heavy use of safely-moveable self-referential types, via move constructors. Bridging move constructors into Rust is one of the great unsolved problems of C++ FFI. Was an unsolved problem. Using a novel interpretation of the Pin guarantees, we'll port all C++ constructors, not just move constructors, to Rust, without compromising Rust's move-after-use protections (something C++ lacks). Return-by-move and collectihttps://speakerdeck.com/rustconf/2021-move-constructors-by-miguel-young-de-la-sotaons are fully supported in today's stable Rust. Beyond zero-cost C++ FFI, Rust "constructors" can be applied to express novel data structures in pure Rust. No C++ knowledge required!

By Miguel Young de la Sota

RustConf

September 14, 2021
Tweet

More Decks by RustConf

Other Decks in Programming

Transcript

  1. error[E0106]: missing lifetime specifier --> <source>:3:15 | 3 | ptr_to_x:

    &mut i32, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct Cycle<’a> { 2 | x: i32, 3 ~ ptr_to_x: &’a mut i32, |
  2. error[E0106]: missing lifetime specifier --> <source>:3:15 | 3 | ptr_to_x:

    &mut i32, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct Cycle { 2 | x: i32, 3 ~ ptr_to_x: &’??? mut i32, |
  3. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; x: 42 p: 0x00000000 cycle @ 0x00000000
  4. x: 42 p: 0x00000000 cycle @ 0x00000000 struct Cycle {

    x: i32, ptr_to_x: *mut i32, } impl Cycle { } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; x: 42 p: 0x7fc55012 cycle @ 0x7fc55012
  5. x: 42 p: 0x00000000 cycle @ 0x00000000 struct Cycle {

    x: i32, ptr_to_x: *mut i32, } impl Cycle { } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; x: 42 p: 0x7fc55012 cycle @ 0x7fc55012
  6. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { fn get(&self) -> Option<i32> { if self.ptr_to_x.is_null() { return None; } unsafe { Some(*self.ptr_to_x) } } } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; let y = cycle.get(); x: 42 p: 0x7fc55012 cycle @ 0x7fc55012
  7. x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 INVARIANT! ptr_to_x is

    always either null or points to x in the same struct. x: 42 p: 0x00000000 cycle @ 0x7fc55012 OK!
  8. x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 INVARIANT! ptr_to_x is

    always either null or points to x in the same struct. x: 42 p: 0x00000000 cycle @ 0x7fc55012 x: 42 p: 0x7fc55012 cycle @ 0x7fc5501a OK! Not allowed!
  9. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { fn new(x: i32) -> Self { let mut cyc = Cycle { x, ptr_to_x: null_mut(), }; cyc.ptr_to_x = &mut cyc.x; cyc } fn get(&self) -> Option<i32> { /* ... */ } } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; let cycle = Cycle::new(42); let y = cycle.get(); x: 42 p: 0x7fc55012 cycle @ 0x7fc55012
  10. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { /* ... */ } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; let cycle = Cycle::new(42); let y = cycle.get(); x: 42 p: 0x7fc55012 cycle @ 0x7fc55000 x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 Callee Stack Caller Stack
  11. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { /* ... */ } let mut cycle = Cycle { x: 42, ptr_to_x: null_mut(), }; cycle.ptr_to_x = &mut cycle.x; let cycle = Cycle::new(42); let y = cycle.get(); // Memory error! x: 42 p: 0x7fc55012 cycle @ 0x7fc55000 x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 Callee Stack Caller Stack
  12. • A Pin<P> wraps a pointer type P (e.g., &mut

    T); unsafe code can assume its address is fixed forever, making it a “pinned pointer”.
  13. • A Pin<P> wraps a pointer type P (e.g., &mut

    T); unsafe code can assume its address is fixed forever, making it a “pinned pointer”. • Pin<P> stops safe code from moving out of it.
  14. • A Pin<P> wraps a pointer type P (e.g., &mut

    T); unsafe code can assume its address is fixed forever, making it a “pinned pointer”. • Pin<P> stops safe code from moving out of it. • Unpin types (i.e. most types) can be moved out of a Pin<P>; non-Unpin types cannot, because they are “address-sensitive”.
  15. • A Pin<P> wraps a pointer type P (e.g., &mut

    T); unsafe code can assume its address is fixed forever, making it a “pinned pointer”. • Pin<P> stops safe code from moving out of it. • Unpin types (i.e. most types) can be moved out of a Pin<P>; non-Unpin types cannot, because they are “address-sensitive”. • Pin<P> is implemented as a library, unlike builtin types like &[T] and *mut i32.
  16. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { fn new(x: i32) -> Self { let mut cyc = Cycle { x, ptr_to_x: null_mut(), }; cyc.ptr_to_x = &mut cyc.x; cyc } fn get(&self) -> Option<i32> { /* ... */ } } Danger!
  17. struct Cycle { x: i32, ptr_to_x: *mut i32, } impl

    Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } } Less Danger
  18. struct Cycle { x: i32, ptr_to_x: *mut i32, _pin: PhantomPinned,

    } impl Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), _pin: PhantomPinned, }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } }
  19. struct Cycle { x: i32, ptr_to_x: *mut i32, _pin: PhantomPinned,

    } impl Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), _pin: PhantomPinned, }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } } let cycle = Cycle::new(42); let y = cycle.get(); // Ok!
  20. struct Cycle { x: i32, ptr_to_x: *mut i32, _pin: PhantomPinned,

    } impl Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), _pin: PhantomPinned, }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } } let cycle = Cycle::new(42); let y = cycle.get(); // Ok! // error: Can’t move out of Pin! // let bad = *cycle;
  21. Pin<P> lets unsafe code assume values won’t ever move. Pin<P>

    stops safe code from accidentally moving them.
  22. struct Cycle { x: i32, ptr_to_x: *mut i32, _pin: PhantomPinned,

    } impl Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), _pin: PhantomPinned, }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } } let cycle = Cycle::new(42); let y = cycle.get(); // Ok! // error: Can’t move out of Pin! // let bad = *cycle;
  23. struct Cycle { x: i32, ptr_to_x: *mut i32, _pin: PhantomPinned,

    } impl Cycle { fn new(x: i32) -> Pin<Box<Self>> { let mut cyc = Box::new(Cycle { x, ptr_to_x: null_mut(), _pin: PhantomPinned, }); cyc.ptr_to_x = &mut cyc.x; Box::into_pin(cyc) } fn get(&self) -> Option<i32> { /* ... */ } } let cycle = Cycle::new(42); let y = cycle.get(); // Ok! // error: Can’t move out of Pin! // let bad = *cycle; Need a pointer to Pin in the first place!
  24. • Can’t return a stack-pinned data, need to Box it.

    • Can’t directly pin data in collections like Vec and HashMap; need to Box them. “Address-sensitive” types feel like second-class citizens in Rust. They’re not zero cost.
  25. C++

  26. x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 INVARIANT! ptr_to_x is

    always either null or points to x in the same struct. x: 42 p: 0x00000000 cycle @ 0x7fc55012 x: 42 p: 0x7fc55012 cycle @ 0x7fc5501a OK! Not allowed! Recall...
  27. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; x: 42 p: 0xTBD *this @ 0xTBD
  28. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; auto cycle = Cycle(42); x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Ctor
  29. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Ctor “raw” constructor
  30. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } /* ... */ }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; struct Cycle { /* ... */ } let mut alloc = MaybeUninit::uninit(); ???.new(&mut alloc); let cycle = unsafe { &*alloc.as_ptr().cast::<Cycle>() }; C++ Rust
  31. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } /* ... */ }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; struct Cycle { /* ... */ } unsafe trait New { type Output; fn new(self, this: &mut MaybeUninit<Self::Output>, ); } let mut alloc = MaybeUninit::uninit(); ???.new(&mut alloc); let cycle = unsafe { &*alloc.as_ptr().cast::<Cycle>() }; C++ Rust
  32. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } /* ... */ }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; struct Cycle { /* ... */ } unsafe trait New { type Output; fn new(self, this: Pin<&mut MaybeUninit<Self::Output>>, ); } let mut alloc = MaybeUninit::uninit(); ???.new(&mut alloc); let cycle = unsafe { &*alloc.as_ptr().cast::<Cycle>() }; C++ Rust
  33. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } /* ... */ }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; struct Cycle { /* ... */ } struct NewCycle(i32); unsafe impl New for NewCycle { type Output = Cycle; fn new(self, this: /* ... */) { this.x = self.0; this.ptr_to_x = &this.x; } } let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle = unsafe { &*alloc.as_ptr().cast::<Cycle>() }; C++ Rust
  34. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } /* ... */ }; char alloc[sizeof(Cycle)]; new (&alloc) Cycle(42); auto cycle = (Cycle*) &alloc; struct Cycle { /* ... */ } struct NewCycle(i32); unsafe impl New for NewCycle { type Output = Cycle; fn new(self, this: /* ... */) { this.x = self.0; this.ptr_to_x = &this.x; } } let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle = unsafe { &*alloc.as_ptr().cast::<Cycle>() }; C++ Rust
  35. • Still can’t return a stack-pinned data; need to Box

    it. • Still can’t directly pin data in collections like Vec and HashMap; need to Box them. “Address-sensitive” types feel like second-class citizens in Rust. Even with Rust constructors! And yet...
  36. class Cycle { /* ... */ }; Cycle SquareCycle(int z)

    { return Cycle(z * z); } x: z * z p: 0xTBD <return> @ 0xTBD
  37. class Cycle { /* ... */ }; Cycle SquareCycle(int z)

    { return Cycle(z * z); } auto cycle = SquareCycle(5); *cycle.ptr_to_x /= 5; // Ok! x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: z * z p: 0xTBD <return> @ 0xTBD SC(5)
  38. class Cycle { /* ... */ }; void SquareCycle(int z,

    Cycle* out) { new (out) Cycle(z * z); } auto cycle; SquareCycle(5, &cycle); *cycle.ptr_to_x /= 5; // Ok! x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: z * z p: 0xTBD <return> @ 0xTBD SC(5)
  39. class Cycle { /* ... */ }; void SquareCycle(int z,

    Cycle* out) { new (out) Cycle(z * z); } // NOTE: Rust has a hidden // return slot, too! auto cycle; SquareCycle(5, &cycle); *cycle.ptr_to_x /= 5; // Ok! x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: z * z p: 0xTBD <return> @ 0xTBD SC(5)
  40. struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } 0x7fc55012

    Slot<T> @ 0x7fc5501a <uninit> [u8] @ 0xsomewhere
  41. struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a,

    T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<&’a mut T>; } 0x7fc55012 Slot<T> @ 0x7fc5501a <uninit> [u8] @ 0x7fc55012
  42. fn new_cycle<’a>( ret: Slot<’a, Cycle>, ) -> Pin<&’a mut Cycle>>

    { ret.emplace(NewCycle(42)) } struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a, T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<&’a mut T>; } 0x7fc55012 Slot<Cycle> @ 0x7fc5501a [42, 00, ..] [u8] @ 0x7fc55012 0x7fc55012 &mut Cycle @ 0x7fc55024 emplace(..)
  43. • Still can’t return a stack-pinned data; need to Box

    it. • Still can’t directly pin data in collections like Vec and HashMap; need to Box them. “Address-sensitive” types feel like second-class citizens in Rust. Even with constructors and slots! Better...
  44. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; auto cycle = Cycle(42); x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Ctor
  45. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; auto cycle = Cycle(42); auto copy = cycle; x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Cycle(int) x: 42 p: 0x7fc55012 copy @ 0x7fc5501a Copy Copy Ctor
  46. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } private: int x_; int* ptr_to_x_; }; auto cycle = Cycle(42); auto copy = cycle; *copy.ptr_to_x_ *= 2; // Modified `cycle`, not `copy`! x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Cycle(int) x: 42 p: 0x7fc55012 copy @ 0x7fc5501a Copy Ctor INVARIANT VIOLATED!
  47. class Cycle { public: Cycle(int x) { this->x_ = x;

    this->ptr_to_x_ = &this->x_; } Cycle(const Cycle& that) { this->x_ = that.x_; this->ptr_to_x_ = &this->x_; } /* ... */ }; auto cycle = Cycle(42); auto copy = cycle; *copy.ptr_to_x_ *= 2; x: 42 p: 0x7fc55012 cycle @ 0x7fc55012 x: 42 p: 0xTBD *this @ 0xTBD Cycle(int) x: 42 p: 0x7fc5501a copy @ 0x7fc5501a Copy Ctor INVARIANT VIOLATED!
  48. C++ variables do not move; you always construct a new

    one! Objects are always notified of copy operations.
  49. class Cycle { public: Cycle(const Cycle& that) { this->x_ =

    that.x; this->ptr_to_x_ = &this->x_; } /* ... */ }; auto cycle = Cycle(42); auto copy = cycle; struct Cycle { /* ... */ } let mut alloc1 = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc1); let cycle = &*alloc1.cast<Cycle>(); let mut alloc2 = MaybeUninit::uninit(); /* copy? */.new(&mut alloc2); let copy = &*alloc2.cast<Cycle>(); C++ Rust
  50. class Cycle { public: Cycle(const Cycle& that) { this->x_ =

    that.x; this->ptr_to_x_ = &this->x_; } /* ... */ }; auto cycle = Cycle(42); auto copy = cycle; struct Cycle { /* ... */ } let mut alloc1 = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc1); let cycle = &*alloc1.cast<Cycle>(); let mut alloc2 = MaybeUninit::uninit(); CloneNew::clone(&cycle, &mut alloc2); let copy = &*alloc2.cast<Cycle>(); C++ Rust
  51. class Cycle { public: Cycle(const Cycle& that) { this->x_ =

    that.x; this->ptr_to_x_ = &this->x_; } /* ... */ }; auto cycle = Cycle(42); auto copy = cycle; struct Cycle { /* ... */ } unsafe trait CloneNew { fn clone(that: &Self, this: Pin<&mut MaybeUninit<Self>>, ); } let mut alloc1 = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc1); let cycle = &*alloc1.cast<Cycle>(); let mut alloc2 = MaybeUninit::uninit(); CloneNew::clone(&cycle, &mut alloc2); let copy = &*alloc2.cast<Cycle>(); C++ Rust
  52. class Cycle { public: Cycle(const Cycle& that) { this->x_ =

    that.x; this->ptr_to_x_ = &this->x_; } /* ... */ }; auto cycle = Cycle(42); auto copy = cycle; struct Cycle { /* ... */ } unsafe impl CloneNew for Cycle { fn clone(that, this) { this.x = that.x; this.ptr_to_x = &this.x; } } let mut alloc1 = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc1); let cycle = &*alloc1.cast<Cycle>(); let mut alloc2 = MaybeUninit::uninit(); CloneNew::clone(&cycle, &mut alloc2); let copy = &*alloc2.cast<Cycle>(); C++ Rust
  53. CloneNew is like a fusion of Clone and New. But

    in Rust, we prefer to move, not copy!
  54. fn new_cycle<’a>( ret: Slot<’a, Cycle>, ) -> Pin<&’a mut Cycle>>

    { ret.emplace(NewCycle(42)) } struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a, T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<&’a mut T>; } 0x7fc55012 Slot<Cycle> @ 0x7fc5501a [42, 00, ..] [u8] @ 0x7fc55012 0x7fc55012 &mut Cycle @ 0x7fc55024 emplace(..)
  55. fn new_cycle<’a>( ret: Slot<’a, Cycle>, ) -> Pin<&’a mut Cycle>>

    { ret.emplace(NewCycle(42)) } struct MoveRef<’a, T>(&’a mut T); struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a, T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<&’a mut T>; } 0x7fc55012 Slot<Cycle> @ 0x7fc5501a [42, 00, ..] [u8] @ 0x7fc55012 0x7fc55012 &mut Cycle @ 0x7fc55024 emplace(..)
  56. fn new_cycle<’a>( ret: Slot<’a, Cycle>, ) -> Pin<&’a mut Cycle>>

    { ret.emplace(NewCycle(42)) } struct MoveRef<’a, T>(&’a mut T); impl<T> Drop for MoveRef<’a, T> { fn drop(&mut self) { ptr::drop_in_place(self.0); } } struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a, T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<&’a mut T>; } 0x7fc55012 Slot<Cycle> @ 0x7fc5501a [42, 00, ..] [u8] @ 0x7fc55012 0x7fc55012 MoveRef<Cycle> @ 0x7fc55024 emplace(..)
  57. fn new_cycle<’a>( ret: Slot<’a, Cycle>, ) -> Pin<MoveRef<’a, Cycle>> {

    ret.emplace(NewCycle(42)) } struct MoveRef<’a, T>(&’a mut T); struct Slot<’a, T> { data: &’a mut MaybeUninit<T>, } impl<’a, T> Slot<’a, T> { fn emplace( self, n: impl New<Output = T>, ) -> Pin<MoveRef<’a, T>>; } 0x7fc55012 Slot<Cycle> @ 0x7fc5501a [42, 00, ..] [u8] @ 0x7fc55012 0x7fc55012 MoveRef<Cycle> @ 0x7fc55024 emplace(..)
  58. unsafe trait CloneNew { fn clone( that: &Self, this: Pin<&mut

    ...>, ); } unsafe impl CloneNew for Cycle { fn clone(that, this) { this.x = that.x; this.ptr_to_x = &this.x; } } Copies
  59. unsafe trait MoveNew { fn mov( that: Pin<MoveRef<Self>>, this: Pin<&mut

    ...>, ); } unsafe trait CloneNew { fn clone( that: &Self, this: Pin<&mut ...>, ); } unsafe impl CloneNew for Cycle { fn clone(that, this) { this.x = that.x; this.ptr_to_x = &this.x; } } Copies Moves
  60. unsafe trait MoveNew { fn mov( that: Pin<MoveRef<Self>>, this: Pin<&mut

    ...>, ); } unsafe impl MoveNew for Cycle { fn mov(that, this) { this.x = that.x; this.ptr_to_x = &this.x; } } unsafe trait CloneNew { fn clone( that: &Self, this: Pin<&mut ...>, ); } unsafe impl CloneNew for Cycle { fn clone(that, this) { this.x = that.x; this.ptr_to_x = &this.x; } } Copies Moves
  61. unsafe trait MoveNew { fn mov( that: Pin<MoveRef<Self>>, this: Pin<&mut

    ...>, ); } unsafe impl<U> MoveNew for U where U: Unpin { fn mov(that, this) { *this = *that; } } unsafe trait CloneNew { fn clone( that: &Self, this: Pin<&mut ...>, ); } unsafe impl<U> CloneNew for U where U: Unpin { fn clone(that, this) { *this = that.clone(); } } Copies Moves
  62. T: MoveNew means we can move from one pinned MoveRef

    into another! It’s generic, so we can build collections that leverage the trait.
  63. • Still can’t return a stack-pinned data; need to Box

    it. • Still can’t directly pin data in collections like Vec and HashMap; need to Box them. New + Slot + MoveRef let us move “address-sensitive” types whenever we need to! Yes, it’s possible!
  64. let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle =

    unsafe { &*alloc.as_ptr().cast::<Cycle>() }; slot!(alloc: Cycle);
  65. let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle =

    unsafe { &*alloc.as_ptr().cast::<Cycle>() }; slot!(alloc: Cycle); let cycle = alloc.emplace(NewCycle(42));
  66. let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle =

    unsafe { &*alloc.as_ptr().cast::<Cycle>() }; slot!(alloc: Cycle); let cycle = alloc.emplace(NewCycle(42)); slot!(alloc: Cycle); let copy = alloc.clone_from(&cycle); slot!(alloc: Cycle); let moved = alloc.move_from(cycle);
  67. let mut alloc = MaybeUninit::uninit(); NewCycle(42).new(&mut alloc); let cycle =

    unsafe { &*alloc.as_ptr().cast::<Cycle>() }; emplace! { let cycle = NewCycle(42); let copy = Clone(&cycle); let moved = Move(cycle); } Possible macro syntax.
  68. Although constructors are a pure-library feature, we can use macros

    to make them feel like part of the language.
  69. What’s Next? • Implementing this in a crate… already done!

    Check out `moveit` on crates.io! • Building Ctor and MoveRef friendly analogues of Vec, HashMap, etc (WIP). • Flesh out concrete pure-Rust and mixed-C++ examples for `moveit`. • Revamp the Pin<P> docs to better express what a Rust “move” is. • QoL features from C++.