1# moveit 2 3A library for safe, in-place construction of Rust (and C++!) objects. 4 5## How It Works 6 7`moveit` revolves around `unsafe trait`s that impose additional guarantees 8on `!Unpin` types, such that they can be moved in the C++ sense. There are 9two senses of "move" frequently used: 10- The Rust sense, which is a blind memcpy and analogous-ish to the 11 C++ "std::is_trivially_moveable` type-trait. Rust moves also render the 12 moved-from object inaccessible. 13- The C++ sense, where a move is really like a mutating `Clone` operation, 14 which leave the moved-from value accessible to be destroyed at the end of 15 the scope. 16 17C++ also has *constructors*, which are special functions that produce a new 18value in a particular location. In particular, C++ constructors may assume 19that the address of `*this` will not change; all C++ objects are effectively 20pinned and new objects must be constructed using copy or move constructors. 21 22The [`New`], [`CopyNew`], and [`MoveNew`] traits bring these concepts 23into Rust. A [`New`] is like a nilary [`FnOnce`], except that instead of 24returning its result, it writes it to a `Pin<&mut MaybeUninit<T>>`, which is 25in the "memory may be repurposed" state described in the 26[`Pin` documentation] (i.e., either it is freshly allocated or the 27destructor was recently run). This allows a [`New`] to rely on the 28pointer's address remaining stable, much like `*this` in C++. 29 30Types that implement [`CopyNew`] may be *copy-constructed*: given any 31pointer to `T: CopyNew`, we can generate a constructor that constructs a 32new, identical `T` at a designated location. [`MoveNew`] types may be 33*move-constructed*: given an *owning* pointer (see [`DerefMove`]) to `T`, 34we can generate a similar constructor, except that it also destroys the 35`T` and the owning pointer's storage. 36 37None of this violates the existing `Pin` guarantees: moving out of a 38`Pin<P>` does not perform a move in the Rust sense, but rather in the C++ 39sense: it mutates through the pinned pointer in a safe manner to construct 40a new `P::Target`, and then destroys the pointer and its contents. 41 42In general, move-constructible types are going to want to be `!Unpin` so 43that they can be self-referential. Self-referential types are one of the 44primary motivations for move constructors. 45 46## Constructors 47 48A constructor is any type that implements [`New`]. Constructors are like 49closures that have guaranteed RVO, which can be used to construct a 50self-referential type in-place. To use the example from the `Pin<T>` docs: 51```rust 52use std::marker::PhantomPinned; 53use std::mem::MaybeUninit; 54use std::pin::Pin; 55use std::ptr; 56use std::ptr::NonNull; 57 58use moveit::new; 59use moveit::new::New; 60use moveit::moveit; 61 62// This is a self-referential struct because the slice field points to the 63// data field. We cannot inform the compiler about that with a normal 64// reference, as this pattern cannot be described with the usual borrowing 65// rules. Instead we use a raw pointer, though one which is known not to be 66// null, as we know it's pointing at the string. 67struct Unmovable { 68 data: String, 69 slice: NonNull<String>, 70 _pin: PhantomPinned, 71} 72 73impl Unmovable { 74 // Defer construction until the final location is known. 75 fn new(data: String) -> impl New<Output = Self> { 76 new::of(Unmovable { 77 data, 78 // We only create the pointer once the data is in place 79 // otherwise it will have already moved before we even started. 80 slice: NonNull::dangling(), 81 _pin: PhantomPinned, 82 }).with(|this| unsafe { 83 let this = this.get_unchecked_mut(); 84 this.slice = NonNull::from(&this.data); 85 }) 86 87 // It is also possible to use other `new::` helpers, such as 88 // `new::by` and `new::by_raw`, to configure construction behavior. 89 } 90} 91 92// The constructor can't be used directly, and needs to be emplaced. 93moveit! { 94 let unmoved = Unmovable::new("hello".to_string()); 95} 96// The pointer should point to the correct location, 97// so long as the struct hasn't moved. 98// Meanwhile, we are free to move the pointer around. 99let mut still_unmoved = unmoved; 100assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); 101 102// Since our type doesn't implement Unpin, this will fail to compile: 103// let mut new_unmoved = Unmovable::new("world".to_string()); 104// std::mem::swap(&mut *still_unmoved, &mut *new_unmoved); 105 106// However, we can implement `MoveNew` to allow it to be "moved" again. 107``` 108 109The [`new`] module provides various helpers for making constructors. As a 110rule, functions which, in Rust, would normally construct and return a value 111should return `impl New` instead. This is analogous to have `async fn`s and 112`.iter()` functions work. 113 114## Emplacement 115 116The example above makes use of the [`moveit!()`] macro, one of many ways to 117turn a constructor into a value. `moveit` gives you two choices for running 118a constructor: 119- On the stack, using the [`MoveRef`] type (this is what [`moveit!()`] 120 generates). 121- On the heap, using the extension methods from the [`Emplace`] trait. 122 123For example, we could have placed the above in a `Box` by writing 124`Box::emplace(Unmovable::new())`. 125 126[`Pin` documentation]: https://doc.rust-lang.org/std/pin/index.html#drop-guarantee 127 128License: Apache-2.0 129 130This is not an officially supported Google product. 131