Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick guide: Non-Rust-Movable Types

This is a short introduction to Crubit’s handling of non-Rust-movable types, and how to use them in Rust. For a more thorough introduction, see crubit.rs/types/non_rust_movable/intro_advanced.

Many, if not most, types in C++ are not Rust-movable. That is, you cannot initialize them using let x = y, you cannot assign them using x = y, and you cannot otherwise relocate the bits in memory, such as by std::mem::swap.

In general, it’s better to make these types Rust-movable. See Rust-movable classes and Making types Rust-movable.

If the class cannot be made Rust-movable, then it will be given a less ergonomic interface, using the Ctor trait.

What is the Ctor trait?

NOTE: The API documentation and source code is at support/ctor.rs

Ctor<Output=T, Error=E> is a trait for implementing lazily evaluated values. These values are constructed in-place, in a C++-compatible way, and pinned. It is typically used as an impl Ctor<Output=T, ...>, which is most easily spelled as Ctor![T] or Ctor![T, Error=...].

Ctor is not a lazily-initialized value itself. It is a value initialization procedure, which returns T upon success and E upon failure. So a Ctor creates a value upon request, which is why we describe it as lazy.

Since exceptions are disabled at Google, we currently only work with Error=Infallible, and for exposition will omit the error type.

Functions accepting and returning a non-Rust movable value in C++ will accept and return an impl Ctor in Rust, as so:

#![allow(unused)]
fn main() {
pub fn accepts_value(x: Ctor![CppType]) {...}
// equivalent to x: impl Ctor<Output=CppType, Error=Infallible>
pub fn returns() -> Ctor![CppType] {...}
}

The easiest way to work with these types in Rust is to box them into a Pin<Box<T>> using Box::emplace(). Then they can be passed by value using mov!(x) (similar to std::move in C++), and by mutable reference using .as_mut(). For example:

#![allow(unused)]
fn main() {
use ctor::{Emplace, mov};  // gives Box::emplace and mov!().
let mut x /* : Pin<Box<T>> */ = Box::emplace(returns());

takes_mutable_reference(x.as_mut());
takes_const_reference(&x);

accepts_value(mov!(x));
}

NOTE: The call to returns() does not construct a value of type CppType, it merely returns a Ctor<Output=CppType>, which is a procedure to lazily create such a value. A CppType is created only when the Ctor is executed as part of the Box::emplace(...) expression in the caller.

If you happen to be directly passing a return value into a parameter, you can avoid the intermediate boxed value:

#![allow(unused)]
fn main() {
accepts_value(returns());
}

If you want to avoid heap allocations in general, you will need to use the more advanced features of ctor.rs. These are described in more detail in the doc comments, and in crubit.rs/types/non_rust_movable/intro_advanced.