Thanks to visit codestin.com
Credit goes to lib.rs

#lifetime #ffi #macro #ownership #no-std

no-std moveit2

A library for safe, in-place construction of Rust (and C++!) objects

5 releases (3 breaking)

Uses new Rust 2024

0.9.0 Apr 13, 2026
0.8.0 Apr 13, 2026
0.7.1 Apr 9, 2026
0.7.0 Apr 9, 2026
0.6.0 Mar 20, 2026

#825 in Rust patterns

Apache-2.0 OR MIT

135KB
1.5K SLoC

moveit2

A library for safe, in-place construction of Rust (and C++!) objects.

This a maintained for of moveit, which has been archived by Google in 2024, with some additional features.

MSRV Policy

Please read this if migrating from moveit!

The MSRV of the original moveit is Rust 1.68. The MSRV policy for this fork is the most recent stable Rust version (in other words, there is no MSRV policy). The MSRV may be bumped as part of minor releases; it is not part of semver.

How It Works

moveit2 revolves around unsafe traits that impose additional guarantees on !Unpin types, such that they can be moved in the C++ sense. There are two senses of "move" frequently used:

  • The Rust sense, which is a blind memcpy and analogous-ish to the C++ "std::is_trivially_moveable` type-trait. Rust moves also render the moved-from object inaccessible.
  • The C++ sense, where a move is really like a mutating Clone operation, which leave the moved-from value accessible to be destroyed at the end of the scope.

C++ also has constructors, which are special functions that produce a new value in a particular location. In particular, C++ constructors may assume that the address of *this will not change; all C++ objects are effectively pinned and new objects must be constructed using copy or move constructors.

The [New], CopyNew, and MoveNew traits bring these concepts into Rust. A [New] is like a nilary FnOnce, except that instead of returning its result, it writes it to a Pin<&mut MaybeUninit<T>>, which is in the "memory may be repurposed" state described in the Pin documentation (i.e., either it is freshly allocated or the destructor was recently run). This allows a [New] to rely on the pointer's address remaining stable, much like *this in C++.

Types that implement CopyNew may be copy-constructed: given any pointer to T: CopyNew, we can generate a constructor that constructs a new, identical T at a designated location. MoveNew types may be move-constructed: given an owning pointer (see DerefMove) to T, we can generate a similar constructor, except that it also destroys the T and the owning pointer's storage.

None of this violates the existing Pin guarantees: moving out of a Pin<P> does not perform a move in the Rust sense, but rather in the C++ sense: it mutates through the pinned pointer in a safe manner to construct a new P::Target, and then destroys the pointer and its contents.

In general, move-constructible types are going to want to be !Unpin so that they can be self-referential. Self-referential types are one of the primary motivations for move constructors.

Constructors

A constructor is any type that implements [New]. Constructors are like closures that have guaranteed RVO, which can be used to construct a self-referential type in-place. To use the example from the Pin<T> docs:

use std::marker::PhantomPinned;
use std::mem::MaybeUninit;
use std::pin::Pin;
use std::ptr;
use std::ptr::NonNull;

use moveit2::new;
use moveit2::new::New;
use moveit2::moveit;

// This is a self-referential struct because the slice field points to the
// data field. We cannot inform the compiler about that with a normal
// reference, as this pattern cannot be described with the usual borrowing
// rules. Instead we use a raw pointer, though one which is known not to be
// null, as we know it's pointing at the string.
struct Unmovable {
  data: String,
  slice: NonNull<String>,
  _pin: PhantomPinned,
}

impl Unmovable {
  // Defer construction until the final location is known.
  fn new(data: String) -> impl New<Self> {
    new::of(Unmovable {
      data,
      // We only create the pointer once the data is in place
      // otherwise it will have already moved before we even started.
      slice: NonNull::dangling(),
      _pin: PhantomPinned,
    }).with(|this| unsafe {
      let this = this.get_unchecked_mut();
      this.slice = NonNull::from(&this.data);
    })

    // It is also possible to use other `new::` helpers, such as
    // `new::by` and `new::by_raw`, to configure construction behavior.
  }
}

// The constructor can't be used directly, and needs to be emplaced.
moveit! {
  let unmoved = Unmovable::new("hello".to_string());
}
// The pointer should point to the correct location,
// so long as the struct hasn't moved.
// Meanwhile, we are free to move the pointer around.
let mut still_unmoved = unmoved;
assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data));

// Since our type doesn't implement Unpin, this will fail to compile:
// let mut new_unmoved = Unmovable::new("world".to_string());
// std::mem::swap(&mut *still_unmoved, &mut *new_unmoved);

// However, we can implement `MoveNew` to allow it to be "moved" again.

The [new] module provides various helpers for making constructors. As a rule, functions which, in Rust, would normally construct and return a value should return impl New instead. This is analogous to have async fns and .iter() functions work.

Emplacement

The example above makes use of the [moveit!()] macro, one of many ways to turn a constructor into a value. moveit2 gives you two choices for running a constructor:

  • On the stack, using the MoveRef type (this is what [moveit!()] generates).
  • On the heap, using the extension methods from the Emplace trait.

For example, we could have placed the above in a Box by writing Box::emplace(Unmovable::new()).

In-place Struct Construction

Outside of new:: helpers, when the ctor feature is enabled this crate also provides a [mod@ctor] module for creating struct constructors that can in-place initialize fields, all without unsafe code. This is primarily done through the Ctor derive macro and [macro@ctor] and try_ctor declarative macros. See the ctor module documentation for more information.

License: Apache-2.0

Dependencies

~0–500KB