blob: 56c6bdeb358d4e7bfdf09cec143a63eaab463075 [file] [view] [edit]
# C++ bindings for Rust functions
C++ code can call functions defined in Rust, provided that the parameter and
return types are supported by Crubit:
* If a parameter or return type is a
[fundamental type](../cpp/fundamental_types.md), then the bindings for the
function use the corresponding C++ type.
* Similarly, if a parameter or return type is a
[pointer type](../cpp/pointer_types.md), then the bindings for the function
use the corresponding C++ pointer type.
* If the type is a user-defined type, such as a [struct](struct.md) or
[enum](enums.md), then the bindings for the function use the bindings for
that type.
As a special case, functions also support reference parameters to supported
types, with some restrictions to ensure safety. See [References](#references).
## Example
Given the following Rust crate:
```live-snippet
cs/file:examples/rust/function/example.rs function:add_two_integers
```
Crubit will generate the following function declaration, which calls into
accompanying glue code:
```live-snippet
cs/file:examples/rust/function/example_generated.h function:add_two_integers
```
## `unsafe` functions
C++ does not have an `unsafe` marker at this time. **In the future**, Crubit may
introduce a way to mark `unsafe` functions to help increase the reliability of
C++ callers.
## References
In general, Rust references are not exposed to C++. However, some Rust functions
which accept reference parameters do get mapped to C++ functions accepting C++
references:
* All references must have an unbound parameter lifetime – not `'static`, for
example.
* Only the parameter itself can be a reference type. References to references,
vectors of references, etc. are still unsupported.
* If there is a `mut` reference parameter, it is the **only** reference
parameter.
This set of rules is intended to describe a safe subset of Rust functions, which
do not introduce substantial aliasing risk to a mixed C++/Rust codebase.
For example, the following Rust functions will receive C++ bindings, and can be
called from C++:
```rust {.good}
fn foo(&self) {}
fn foo(_: &mut i32) {}
fn foo(_: &i32, _: &i32) {}
```
However, none of the below will receive bindings:
```rust {.bad}
fn foo(_: &'static i32) {} // 'static lifetime is bound
fn foo(_: &&i32) {} // Reference in non-parameter type
fn foo(_: &mut i32, _: &i32) {} // More than one reference, one of which is mut
fn foo(_: &'a i32) {} // 'a is not a lifetime parameter of `foo`
```
Returned references are mapped to C++ references (for `self` borrows) or C++
pointers, depending on the lifetime.
If you wish to accept more than one reference/pointer in C++, a raw pointer
(`*const T`, `*mut T`) can be used instead. However, all of the usual `unsafe`
caveats apply.
## Slice parameters
Rust slice parameters (`&[T]`, `&mut [T]`, `&str`) map to `rs::SliceRef<const
T>`, `rs::SliceRef<T>`, and `rs::StrRef` in C++.
C++ callers can pass `absl::Span<const T>`, `std::span<const T>`, or contiguous
containers like `std::vector<T>` and `std::array<T, N>` directly to satisfy
`&[T]` slice parameters with no conversion boilerplate:
```rust {.good}
fn process_text(_: &str) {} // C++ callers can pass "hello" or std::string_view
fn process_bytes(_: &[u8]) {} // C++ callers can pass absl::Span<const uint8_t>, std::span, "hello", or std::string_view
fn process_items(_: &[i32]) {} // C++ callers can pass absl::Span<const int32_t>, std::span, std::vector, etc.
```
However, mutable slices cannot be implicitly constructed from read-only views or
string literals:
```rust {.bad}
fn process_mut_bytes(_: &mut [u8]) {} // Cannot pass string literals or const spans to mutable byte slices
```
* **`&[T]`**: Maps to `rs::SliceRef<const T>`
(`crubit/support/rs_std/slice_ref.h`). Converts implicitly from
`absl::Span<const T>`, `absl::Span<T>`, `std::span<const T>`,
`std::span<T>`, `std::vector<T>`, and `std::array<T, N>`.
* **`&mut [T]`**: Maps to `rs::SliceRef<T>`
(`crubit/support/rs_std/slice_ref.h`). Converts implicitly from mutable
`std::span<T>` and `absl::Span<T>`, or construct via `rs::SliceRef(ptr,
size)`.
* **`&str`**: Maps to `rs::StrRef` (`crubit/support/rs_std/str_ref.h`).
Converts from string literals and `std::string_view`.
* **`&[u8]`**: Maps to `rs::SliceRef<const uint8_t>`. In addition to
`absl::Span` and `std::span`, also converts from string literals and
`std::string_view`.
## Generic functions
Crubit doesn't support generating bindings for *arbitrary* generic functions.
Nevertheless, a small set of generic type parameters is supported by using
canonical substitutions which are defined as follows:
Generic constraints | Canonical substitution
------------------------------- | ----------------------
`T: AsMut<U>` | `&mut U`
`T: AsRef<U>` | `&U`
`T: Into<U>` | `U`
`F: Fn(Args...) -> R` | `rs::FnRef<R(Args...) const>`
`F: FnMut(Args...) -> R` | `rs::FnRef<R(Args...)>`
`F: FnOnce(Args...) -> R + 'static` | `rs::Fn<R(Args...) &&>`
## Callbacks (`Fn`, `FnMut`, `FnOnce`) {#callbacks}
Rust functions can accept callbacks as borrowed trait objects (`&dyn Fn`, `&mut dyn FnMut`),
owned boxes (`Box<dyn Fn>`, `Box<dyn FnMut>`, `Box<dyn FnOnce>`), or generic `impl Trait`
parameters:
* **`&dyn Fn(...)` / `impl Fn(...)`**: Maps to `rs::FnRef<... const>` (borrowed, no allocation).
* **`&mut dyn FnMut(...)` / `impl FnMut(...)`**: Maps to `rs::FnRef<...>` (borrowed, no allocation).
* **`impl FnOnce(...)`** (without `'static`): Maps to `rs::FnRef<...>`
(borrowed, no allocation).
* **`Box<dyn Fn(...)>`**: Maps to `rs::Fn<... const>` (owned).
* **`Box<dyn FnMut(...)>`**: Maps to `rs::Fn<...>` (owned mutable).
* **`Box<dyn FnOnce(...)>` / `impl FnOnce(...) + 'static`**: Maps to `rs::Fn<... &&>` (owned, single-use).
C++ callers can satisfy these parameters by passing C++ lambdas, function pointers,
or `absl::AnyInvocable`. See [Callbacks](callbacks.md) for full
details.