Rust bindings for C++ standard library (std) types

When generating Rust bindings for a C++ target, bindings to the C++ standard library types are provided by the cpp_std support library (which re-exports the legacy cc_std::std::*). This contains a combination of manually written types and automatically generated bindings.

You can then use standard library types directly via cpp_std::{sometype}, e.g., cpp_std::unique_ptr<T>.

TODO(b/454337872): The following is stub documentation in the absence of rustdoc support. Instead of linking to API docs, we link to Rust source files.

std::string

TODO(b/408961701): Bindings for the real std::string type.

std::string is only supported as a “bridge type”, with runtime conversion. When a std::string is passed or returned by value, it is transformed into a cpp_std::string_wrapper.

API: support/cc_std_impl/string.rs

std::string_view and absl::string_view {#string_view}

C++'s std::string_view type has two analogues in Rust. If no lifetime can be specified or inferred, or the data may be mutably aliased, then one should use cpp_std::raw_string_view. This is analogous to a *const [u8] in Rust. Otherwise, cpp_std::string_view<'a>.

API: support/cc_std_impl/string_view.rs

std::unique_ptr<T> {#unique_ptr}

C++'s std::unique_ptr<T> type has two analogues in Rust. If the type has a virtual destructor, or an overloaded operator delete, then it becomes a cpp_std::virtual_unique_ptr<T>. In particular, polymorphic types will be stored in a cpp_std::virtual_unique_ptr<T>.

Otherwise, when the destructor is directly callable and not virtual, it becomes a cpp_std::unique_ptr<T>.

For example, a C++ std::unique_ptr<int> is a Rust cpp_std::unique_ptr<int>, but a C++ std::unique_ptr<ios_base> would become a Rust cpp_std::virtual_unique_ptr<ios_base>.

Both types implement Deref<Target = T> and DerefMut<Target = T> (when T: Unpin). While C++ std::unique_ptr is only shallow-const (i.e. const std::unique_ptr<T>& allows mutating the underlying T), it is conventionally treated as deep-const. In order for &unique_ptr<T> to be usable at all from Rust, we treat it as deep-const. C++ code which mutates a value of type T while Rust has obtained a &T via &unique_ptr<T> -> &T deref will result in undefined behavior.

API: support/cc_std_impl/unique_ptr.rs

See also: crubit.rs/errors/delete

std::shared_ptr<T> {#shared_ptr}

A C++ std::shared_ptr<T> becomes a Rust cpp_std::shared_ptr<T>. It implements Deref<Target = T>, allowing &shared_ptr<T> to be dereferenced to &T.

API: support/cc_std_impl/shared_ptr.rs

std::vector<T>

A C++ std::vector<T> becomes a Rust cpp_std::vector<T>.

API: support/cc_std_impl/vector.rs

std::optional<T>

By default, std::optional<T> is a “bridge type”, with runtime conversion: when it is passed or returned by value, it is transformed into a Rust Option<T>.

In positions which cannot hold a bridged type, such as struct fields, pointees, and template arguments, std::optional<T> is instead the layout-compatible cpp_std::optional<T>, or cpp_std::trivial_optional<T> when T is Copy (mirroring C++, where std::optional<T> is trivially copyable and trivially destructible whenever T is).

To use the layout-compatible type in by-value positions as well, enable the layout_compat_optional Crubit feature on your target. This is a breaking change for code which relies on the bridged Option<T>, so it is opt-in.

API: support/cc_std_impl/optional.rs