blob: 8b0d7b83ff4efd964078a67fdd36d495df38a70d [file] [view] [edit]
# C++ bindings for Rust standard library (`std`) types
When generating C++ bindings for a Rust crate (via `cpp_api_from_rust`) bindings
to the Rust standard library types (`std`, `alloc`, and `core`) are
automatically generated for those types under the namespaces `rs::std`,
`rs::alloc`, and `rs::core`.
## Working with `String` in C++ {#string}
Rust's `String` becomes `rs::std::string::String` in C++ (also spelled
`rs::alloc::string::String`). It can be constructed from a C++ string literal:
```c++
rs::std::string::String s("hello, world!");
```
An existing `std::string_view` (or `absl::string_view`) can be converted to a
Rust `String` using `rs::StrRef::FromUtf8`:
```c++
void AcceptsStringView(std::string_view view) {
std::optional<rs::StrRef> str_ref = rs::StrRef::FromUtf8(view);
if (!str_ref.has_value()) {
return;
}
rs::std::string::String s(*str_ref);
// Continue on with using your Rust String...
}
```
An instance of `String` can be converted back to a `std::string` by calling
`.as_str()` and using the resulting `rs::StrRef` to construct a `std::string`:
```c++
std::string s2(s.as_str());
EXPECT_EQ(s2, "hello, world!");
```
`rs::StrRef` supports implicit conversion to `std::string_view` to make this
work.
A Rust method that takes an `&mut String` such as:
```rust
pub fn append_to_rust_string(val: &mut String, s: &str) {
val.push_str(s);
}
```
becomes a C++ method taking a reference to a `rs::std::string::String`:
```c++
// You could just as easily call `push_str` directly. It receives Crubit
// bindings. We use a wrapper method here for expository purposes.
void append_to_rust_string(rs::std::string::String& val, rs::StrRef s);
```
Allowing for C++ to call it:
```c++
append_to_rust_string(s, " I'm a neat addition");
```
## `Result`
The Rust `Result<T, E>` generic receives C++ bindings as `rs::Result<T, E>`, so
long as both `T` and `E` are non-ZST types supported by Crubit.
`rs::Result<T, E>` has a similar API to
[`std::expected`](https://en.cppreference.com/cpp/utility/expected).
(Alternatively, it has a similar API to
[`std::optional`](https://en.cppreference.com/cpp/utility/optional), but with an
additional error payload.) If `has_value()` returns `false`, then `error()` will
return a reference to the error payload of type `E`.
```c++
if (myresult.has_value()) {
Foo(*myresult);
} else {
Foo(myresult.error());
}
```
A more complete description of the API is in the common `ResultBase` public base
class: support/rs_std/result.h
## `Option`
The Rust `Option<T>` generic receives C++ bindings as `rs::Option<T>`, so long
as `T` is a non-ZST type supported by Crubit. It has a similar API to
[`std::optional`](https://en.cppreference.com/cpp/utility/optional), and
implicitly converts to and from `std::optional`.
Unlike `std::optional`, `rs::Option` has no `operator bool`, so check for a
value with `has_value()` rather than `if (myoption)`. Conversion to
`std::optional` is only available from an rvalue, so either convert the result
of a call directly or `std::move` an existing `rs::Option`:
```c++
if (myoption.has_value()) {
Foo(*myoption);
}
std::optional<int32_t> converted = std::move(myoption);
std::optional<int32_t> direct = rust_crate::find_index(name);
```
A more complete description of the API is in the common `OptionBase` public base
class: support/rs_std/option.h
## `Tuple`
A Rust tuple `(T1, T2, ...)` receives C++ bindings as `rs::Tuple<T1, T2, ...>`.
It can be constructed from its elements (including with brace-initialization,
e.g. `{a, b}`) when all elements are move-constructible in C++, or converted
from a `std::tuple<T1, T2, ...>`. Elements are accessed with `rs::get<I>` or
structured bindings:
```c++
rs::Tuple<int32_t, int32_t> pair = {1, 2};
auto [first, second] = pair;
```
The support header is support/rs_std/tuple.h.
## `Vec`
The Rust `Vec<T>` generic receives C++ bindings as `rs::Vec<T>`, so long as `T`
is a type supported by Crubit. Each `rs::Vec<T>` is a generated specialization
that inherits its public API from the common `VecBase<T>` base class in
support/rs_std/vec.h. The API is modeled on
[`std::vector`](https://en.cppreference.com/cpp/container/vector), with a few
differences noted below.
`rs::Vec<T>` is a real Rust `Vec` under the hood: it has the same layout, and
its buffer is allocated and freed through Rust's global allocator. A `Vec`
created in C++ can therefore be handed to Rust (and vice versa) without any
copying or conversion.
### Element access and iteration
`rs::Vec<T>` provides the usual contiguous-container accessors:
* `size()`, `capacity()`, `empty()`
* `data()` (returns a pointer to the contiguous element buffer)
* `operator[]`, `front()`, `back()` -- these are **bounds-checked** and abort
the process on misuse (e.g. an out-of-range index or `front()` on an empty
vector), mirroring the panicking behavior of Rust's `Vec`.
* `begin()` / `end()`, `cbegin()` / `cend()`, `rbegin()` / `rend()`,
`crbegin()` / `crend()`
Iterators are raw pointers, so `rs::Vec<T>` works with range-based `for` loops
and standard algorithms, and satisfies `std::ranges::contiguous_range`. This
also makes it implicitly convertible to `std::span<const T>` / `std::span<T>`
and, by extension, to `rs::SliceRef<const T>` / `rs::SliceRef<T>`:
```c++
rs::Vec<int32_t> v = my_crate::return_vec();
for (int32_t& x : v) {
x += 1;
}
int32_t sum = std::accumulate(v.begin(), v.end(), 0);
std::span<const int32_t> span = v;
rs::SliceRef<const int32_t> slice = v;
```
### Modifying a `Vec` from C++
Elements can be added, removed, and constructed in place from C++:
```c++
rs::Vec<int32_t> v;
v.reserve(3); // Total capacity, like std::vector::reserve.
v.push_back(1);
v.push_back(2);
v.emplace_back(3); // Constructs in place; returns a T&.
v.insert(0, 0); // Inserts at *index* 0, shifting the rest right.
v.pop_back(); // Removes and destroys the last element.
v.clear(); // Destroys all elements but keeps the buffer.
```
Differences from `std::vector` worth knowing about:
* `insert(index, value)` takes an index rather than an iterator (as in Rust's
`Vec::insert`).
* `reserve(new_cap)` has `std::vector::reserve` semantics: it ensures the
*total* capacity is at least `new_cap`. To request room for `n` *additional*
elements (Rust's `Vec::reserve(additional)` semantics), use
`reserve_additional_capacity(n)` instead.
* Only the operations listed above are currently supported. In particular,
there is no `erase`, `resize`, or range `insert`. For other operations,
write a small Rust helper function and call it through its Crubit bindings.
## Availability of generic `rs_std` specializations
Generic standard library types such as `rs::Option<T>`, `rs::Result<T, E>`,
`rs::Tuple<Ts...>`, and `rs::Vec<T>` are only defined for type arguments that
are actually used in a Crubit-bound Rust API. The support headers (`option.h`,
`result.h`, `tuple.h`, `vec.h`) provide only the generic base implementations
(`OptionBase`, `ResultBase`, `VecBase`, etc.) and leave the primary template
uninstantiable; the concrete specialization for a given set of type arguments is
emitted into the generated header of a crate whose public API mentions that
instantiation. Attempting to use one of these templates with type arguments that
do not appear in any of the Rust APIs you depend on is a compile-time error.
## Callables (`Fn`, `FnMut`, `FnOnce`) {#callables}
Rust function traits and callable parameters receive bindings through
`rs::FnRef` and `rs::Fn`:
* **`rs::FnRef<Sig>`** (`support/rs_std/fn_ref.h`):
Non-owning, borrowed view of a callable corresponding to `&dyn Fn` or `&mut
dyn FnMut` (or generic `impl Fn` / `impl FnMut`). Does not allocate on the
heap.
* **`rs::Fn<Sig>`** (`support/rs_std/fn.h`): Owning,
heap-allocated callable corresponding to `Box<dyn Fn>`, `Box<dyn FnMut>`, or
`Box<dyn FnOnce>` (or generic `impl FnOnce + 'static`). `rs::Fn<Sig &&>`
corresponds to `FnOnce` and must be invoked via `std::move(f)(...)`.
* **`rs::DynCallable<Sig>`**
(`support/rs_std/dyn_callable.h`): Move-only wrapper for
Rust `Box<dyn Trait>` objects received in C++, implicitly convertible to
`absl::AnyInvocable<Sig>`.
See [Callbacks](http://crubit.rs/types/callbacks) for the complete guide and
examples.
## `Box`
The Rust `Box<T>`, `Pin<Box<T>>`, `Box<[T]>`, and `Box<str>` types receive C++
bindings as `rs::Box<T>`, `rs::PinBox<T>`, `rs::SliceBox<T>`, and `rs::StrBox`.
See [Box types](../types/box.md) for more details.