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.