From 722a275ff71fd5fb5cc56fb419911bd8ead697d9 Mon Sep 17 00:00:00 2001 From: Devin Jeanpierre Date: Wed, 22 Jul 2026 05:24:23 -0700 Subject: [PATCH] Attempt to document Result/Option. I was reviewing a CL where someone tried to use Crubit, and they didn't use Result, presumably because they assumed it wouldn't work. PiperOrigin-RevId: 952053853 --- docs/rust/std.md | 41 ++- examples/rust/std/BUILD | 45 +++ examples/rust/std/example.rs | 11 + examples/rust/std/example_generated.h | 384 ++++++++++++++++++++++++++ examples/rust/std/main.cc | 25 ++ 5 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 examples/rust/std/BUILD create mode 100644 examples/rust/std/example.rs create mode 100644 examples/rust/std/example_generated.h create mode 100644 examples/rust/std/main.cc diff --git a/docs/rust/std.md b/docs/rust/std.md index 733530cc4..bf60ef7c4 100644 --- a/docs/rust/std.md +++ b/docs/rust/std.md @@ -56,4 +56,43 @@ Allowing for C++ to call it: ```c++ append_to_rust_string(s, " I'm a neat addition"); -``` \ No newline at end of file +``` + +## `Result` + +The Rust `Result` generic receives Rust bindings as `rs_std::Result`, so long as both `T` and `E` are non-ZST types supported by Crubit. + +`rs_std::Result` 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` generic receives Rust bindings as `rs_std::Option`, 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`. + +```c++ +if (myoption.has_value()) { + Foo(*myoption); +} +``` + +A more complete description of the API is in the common `OptionBase` public base +class: support/rs_std/option.h diff --git a/examples/rust/std/BUILD b/examples/rust/std/BUILD new file mode 100644 index 000000000..0252e5419 --- /dev/null +++ b/examples/rust/std/BUILD @@ -0,0 +1,45 @@ +load("@rules_cc//cc:cc_binary.bzl", "cc_binary") +load( + "@rules_rust//rust:defs.bzl", + "rust_library", +) +load( + "//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl", + "cc_bindings_from_rust", +) +load( + "//cc_bindings_from_rs/test/golden:golden_test.bzl", + "golden_test", +) + +package(default_applicable_licenses = ["//:license"]) + +licenses(["notice"]) + +# This declares an "example_crate_cc_api" target that provides Crubit-generated +# C++ bindings for the Rust crate behind the `":example_crate"` target. +rust_library( + name = "example_crate", + srcs = ["example.rs"], +) + +cc_bindings_from_rust( + name = "example_crate_cc_api", + crate = ":example_crate", +) + +cc_binary( + name = "main", + srcs = ["main.cc"], + deps = [ + ":example_crate_cc_api", + "//support/rs_std:rs_alloc", + "//support/rs_std:str_ref", + ], +) + +golden_test( + name = "example_golden_test", + golden_h = "example_generated.h", + rust_library = "example_crate", +) diff --git a/examples/rust/std/example.rs b/examples/rust/std/example.rs new file mode 100644 index 000000000..22b4277af --- /dev/null +++ b/examples/rust/std/example.rs @@ -0,0 +1,11 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +pub fn returns_result(is_ok: bool) -> Result, String> { + if is_ok { + Ok(Some("hello world!")) + } else { + Err("goodbye, cruel world!".to_string()) + } +} diff --git a/examples/rust/std/example_generated.h b/examples/rust/std/example_generated.h new file mode 100644 index 000000000..9bd3e01b0 --- /dev/null +++ b/examples/rust/std/example_generated.h @@ -0,0 +1,384 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Automatically @generated C++ bindings for the following Rust crate: +// example_crate_golden + +// clang-format off +#ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STD_EXAMPLE_CRATE_GOLDEN +#define THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STD_EXAMPLE_CRATE_GOLDEN + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#pragma clang diagnostic ignored "-Wunused-private-field" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wignored-attributes" +#include "support/internal/slot.h" +#include "support/rs_std/option.h" +#include "support/rs_std/result.h" +#include "support/rs_std/str_ref.h" + +#include +#include +#include +#include +#include +#include + +#include "support/rs_std/rs_alloc.h" + +namespace example_crate { + +rs_std::Result, ::rs::alloc::string::String> +returns_result(bool is_ok); + +} + +#ifndef _CRUBIT_BINDINGS_FOR_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e +#define _CRUBIT_BINDINGS_FOR_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e +template <> +struct alignas(8) CRUBIT_INTERNAL_RUST_TYPE( + "std :: option :: Option < & 'static str >") rs_std::Option + : public rs_std::OptionBase, + rs_std::StrRef> { + public: + // Rust types that are `Copy` get trivial, `default` C++ copy constructor and + // assignment operator. + Option(const Option&) = default; + Option& operator=(const Option&) = default; + Option(Option&&) = default; + Option& operator=(Option&&) = default; + + Option(::crubit::UnsafeRelocateTag, Option&& value); + using base_type = + rs_std::OptionBase, rs_std::StrRef>; + constexpr Option() = default; + constexpr Option(::std::nullopt_t) noexcept; + constexpr Option& operator=(::std::nullopt_t) noexcept; + template + requires(rs_std::OptionForwardConstructible) + Option(U&& value) noexcept; + template + requires(rs_std::OptionForwardConstructible) + Option& operator=(U&& value) noexcept; + template + requires(rs_std::OptionFromStdOptional) + Option(Opt&& value) noexcept; + template + requires(rs_std::OptionFromStdOptional) + Option& operator=(Opt&& value) noexcept; + template + explicit Option(::std::in_place_t ip, Args&&... args) noexcept; + ~Option() noexcept = default; + + private: + friend base_type; + using tag_type = ::std::uint64_t; + static constexpr tag_type kNoneVal = 0; + rs_std::StrRef* some_ptr() noexcept { + return reinterpret_cast(storage_); + } + rs_std::StrRef const* some_const_ptr() const noexcept { + return reinterpret_cast(storage_); + } + void set_some_tag() noexcept {} + constexpr void set_none_tag() noexcept { set_tag(kNoneVal); } + constexpr bool is_none() const noexcept { return tag() == kNoneVal; } + constexpr ::std::uint64_t tag() const& noexcept; + constexpr void set_tag(::std::uint64_t tag) noexcept; + + private: + unsigned char storage_[16]; +}; +#endif + +#ifndef _CRUBIT_BINDINGS_FOR_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Result_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e_x00000020_x0000002c_x00000020_x0000003a_x0000003a_x00000020rs_x00000020_x0000003a_x0000003a_x00000020alloc_x00000020_x0000003a_x0000003a_x00000020string_x00000020_x0000003a_x0000003a_x00000020String_x00000020_x0000003e +#define _CRUBIT_BINDINGS_FOR_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Result_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e_x00000020_x0000002c_x00000020_x0000003a_x0000003a_x00000020rs_x00000020_x0000003a_x0000003a_x00000020alloc_x00000020_x0000003a_x0000003a_x00000020string_x00000020_x0000003a_x0000003a_x00000020String_x00000020_x0000003e +template <> +struct alignas(8) CRUBIT_INTERNAL_RUST_TYPE( + "std :: result :: Result < :: core :: option :: Option < & 'static str > , " + ":: alloc :: string :: String >") + rs_std::Result, ::rs::alloc::string::String> + : public rs_std::ResultBase, + ::rs::alloc::string::String>, + rs_std::Option, + ::rs::alloc::string::String> { + public: + // Clone::clone + Result(const Result&); + + // Clone::clone_from + rs_std::Result, ::rs::alloc::string::String>& + operator=(const Result&); + + Result(::crubit::UnsafeRelocateTag, Result&& value); + + public: + using base_type = + rs_std::ResultBase, + ::rs::alloc::string::String>, + rs_std::Option, + ::rs::alloc::string::String>; + template + requires(rs_std::ResultForwardConstructible< + Result, rs_std::Option, U>) + explicit constexpr Result(U&& ok) noexcept; + template + requires(rs_std::ResultForwardConstructible< + Result, rs_std::Option, U>) + constexpr Result& operator=(U&& ok) noexcept; + template + requires( + rs_std::ResultUnexpectedConstructible<::rs::alloc::string::String, F>) + explicit constexpr Result(rs_std::unexpected&& err) noexcept; + template + requires( + rs_std::ResultUnexpectedConstructible<::rs::alloc::string::String, F>) + constexpr Result& operator=(rs_std::unexpected&& err) noexcept; + template + explicit constexpr Result(::std::in_place_t ip, Args&&... args) noexcept; + template + explicit constexpr Result(rs_std::unexpect_t u, Args&&... args) noexcept; + ~Result() noexcept; + + private: + friend base_type; + bool has_value_impl() const noexcept { + return tag() == UINT64_C(18446744073709551615); + } + rs_std::Option* ok_ptr() noexcept { + return reinterpret_cast*>(__storage + 8); + } + rs_std::Option const* ok_const_ptr() const noexcept { + return reinterpret_cast const*>(__storage + + 8); + } + ::rs::alloc::string::String* err_ptr() noexcept { + return reinterpret_cast<::rs::alloc::string::String*>(__storage); + } + ::rs::alloc::string::String const* err_const_ptr() const noexcept { + return reinterpret_cast<::rs::alloc::string::String const*>(__storage); + } + void set_ok_tag() noexcept { set_tag(UINT64_C(18446744073709551615)); } + void set_err_tag() noexcept {} + constexpr ::std::uint64_t tag() const& noexcept; + constexpr void set_tag(::std::uint64_t tag) noexcept; + + private: + unsigned char __storage[24]; +}; +#endif + +namespace example_crate { + +namespace __crubit_internal { +extern "C" void __crubit_thunk_returns_uresult( + bool, + rs_std::Result, ::rs::alloc::string::String>* + __ret_ptr); +} +inline rs_std::Result, + ::rs::alloc::string::String> +returns_result(bool is_ok) { + crubit::Slot, + ::rs::alloc::string::String>> + __return_value_ret_val_holder; + auto* __return_value_storage = __return_value_ret_val_holder.Get(); + __crubit_internal::__crubit_thunk_returns_uresult(is_ok, + __return_value_storage); + return ::std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue(); +} + +} // namespace example_crate + +#ifndef _CRUBIT_BINDINGS_FOR_IMPL_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e +#define _CRUBIT_BINDINGS_FOR_IMPL_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e +static_assert( + ::std::is_trivially_copy_constructible_v>); +static_assert( + ::std::is_trivially_copy_assignable_v>); +static_assert( + ::std::is_trivially_move_constructible_v>); +static_assert( + ::std::is_trivially_move_assignable_v>); +inline rs_std::Option::Option(::crubit::UnsafeRelocateTag, + Option&& value) { + ::std::memcpy(this, &value, sizeof(value)); +} +static_assert( + ::std::is_trivially_destructible_v>); +inline constexpr ::std::uint64_t rs_std::Option::tag() + const& noexcept { + ::std::array __bytes = {}; + for (::std::size_t i = 0; i < sizeof(::std::uint64_t); ++i) { + __bytes[i] = storage_[0 + i]; + } + return ::std::bit_cast<::std::uint64_t>(__bytes); +} +inline constexpr void rs_std::Option::set_tag( + ::std::uint64_t tag) noexcept { + auto __bytes = + ::std::bit_cast<::std::array>( + tag); + for (::std::size_t i = 0; i < sizeof(::std::uint64_t); ++i) { + storage_[0 + i] = __bytes[i]; + } +} + +inline constexpr rs_std::Option::Option( + ::std::nullopt_t) noexcept + : base_type(::std::nullopt) {} +inline constexpr rs_std::Option& +rs_std::Option::operator=(::std::nullopt_t) noexcept { + base_type::operator=(::std::nullopt); + return *this; +} +template + requires(rs_std::OptionForwardConstructible, + rs_std::StrRef, U>) +inline rs_std::Option::Option(U&& value) noexcept + : base_type(::std::forward(value)) {} +template + requires(rs_std::OptionForwardConstructible, + rs_std::StrRef, U>) +inline rs_std::Option& +rs_std::Option::operator=(U&& value) noexcept { + base_type::operator=(::std::forward(value)); + return *this; +} +template + requires(rs_std::OptionFromStdOptional) +inline rs_std::Option::Option(Opt&& value) noexcept + : base_type(::std::forward(value)) {} +template + requires(rs_std::OptionFromStdOptional) +inline rs_std::Option& +rs_std::Option::operator=(Opt&& value) noexcept { + base_type::operator=(::std::forward(value)); + return *this; +} +template +inline rs_std::Option::Option(::std::in_place_t ip, + Args&&... args) noexcept + : base_type(ip, ::std::forward(args)...) {} + +#endif + +#ifndef _CRUBIT_BINDINGS_FOR_IMPL_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Result_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e_x00000020_x0000002c_x00000020_x0000003a_x0000003a_x00000020rs_x00000020_x0000003a_x0000003a_x00000020alloc_x00000020_x0000003a_x0000003a_x00000020string_x00000020_x0000003a_x0000003a_x00000020String_x00000020_x0000003e +#define _CRUBIT_BINDINGS_FOR_IMPL_rs_ustd_x00000020_x0000003a_x0000003a_x00000020Result_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020Option_x00000020_x0000003c_x00000020rs_ustd_x00000020_x0000003a_x0000003a_x00000020StrRef_x00000020_x0000003e_x00000020_x0000002c_x00000020_x0000003a_x0000003a_x00000020rs_x00000020_x0000003a_x0000003a_x00000020alloc_x00000020_x0000003a_x0000003a_x00000020string_x00000020_x0000003a_x0000003a_x00000020String_x00000020_x0000003e +namespace __crubit_internal { +extern "C" void +__crubit_thunk_Clone_uclone_ustd_x0000003a_x0000003aresult_x0000003a_x0000003aResult_x0000003cstd_x0000003a_x0000003aoption_x0000003a_x0000003aOption_x0000003c_x00000026_x00000027static_x00000020str_x0000003e_x0000002c_x00000020std_x0000003a_x0000003astring_x0000003a_x0000003aString_x0000003e( + rs_std::Result, + ::rs::alloc::string::String> const&, + rs_std::Result, ::rs::alloc::string::String>* + __ret_ptr); +} +namespace __crubit_internal { +extern "C" void +__crubit_thunk_Clone_uclone_ufrom_ustd_x0000003a_x0000003aresult_x0000003a_x0000003aResult_x0000003cstd_x0000003a_x0000003aoption_x0000003a_x0000003aOption_x0000003c_x00000026_x00000027static_x00000020str_x0000003e_x0000002c_x00000020std_x0000003a_x0000003astring_x0000003a_x0000003aString_x0000003e( + rs_std::Result, + ::rs::alloc::string::String>&, + rs_std::Result, + ::rs::alloc::string::String> const&); +} +inline rs_std::Result, + ::rs::alloc::string::String>::Result(const Result& + other) { + __crubit_internal:: + __crubit_thunk_Clone_uclone_ustd_x0000003a_x0000003aresult_x0000003a_x0000003aResult_x0000003cstd_x0000003a_x0000003aoption_x0000003a_x0000003aOption_x0000003c_x00000026_x00000027static_x00000020str_x0000003e_x0000002c_x00000020std_x0000003a_x0000003astring_x0000003a_x0000003aString_x0000003e( + other, this); +} +inline rs_std::Result, + ::rs::alloc::string::String>& +rs_std::Result, + ::rs::alloc::string::String>::operator=(const Result& other) { + if (this != &other) { + __crubit_internal:: + __crubit_thunk_Clone_uclone_ufrom_ustd_x0000003a_x0000003aresult_x0000003a_x0000003aResult_x0000003cstd_x0000003a_x0000003aoption_x0000003a_x0000003aOption_x0000003c_x00000026_x00000027static_x00000020str_x0000003e_x0000002c_x00000020std_x0000003a_x0000003astring_x0000003a_x0000003aString_x0000003e( + *this, other); + } + return *this; +} +inline rs_std:: + Result, ::rs::alloc::string::String>::Result( + ::crubit::UnsafeRelocateTag, Result&& value) { + ::std::memcpy(this, &value, sizeof(value)); +} +inline rs_std::Result, + ::rs::alloc::string::String>::~Result() noexcept { + this->Reset(); +} +inline constexpr ::std::uint64_t +rs_std::Result, + ::rs::alloc::string::String>::tag() const& noexcept { + std::array __bytes = {}; + for (std::size_t i = 0; i < sizeof(::std::uint64_t); ++i) { + __bytes[i] = __storage[0 + i]; + } + return std::bit_cast<::std::uint64_t>(__bytes); +} +inline constexpr void rs_std::Result< + rs_std::Option, + ::rs::alloc::string::String>::set_tag(::std::uint64_t tag) noexcept { + auto __bytes = + std::bit_cast>(tag); + for (std::size_t i = 0; i < sizeof(::std::uint64_t); ++i) { + __storage[0 + i] = __bytes[i]; + } +} + +template + requires(rs_std::ResultForwardConstructible< + rs_std::Result, + ::rs::alloc::string::String>, + rs_std::Option, U>) +inline constexpr rs_std::Result< + rs_std::Option, + ::rs::alloc::string::String>::Result(U&& ok) noexcept + : base_type(::std::forward(ok)) {} +template + requires(rs_std::ResultForwardConstructible< + rs_std::Result, + ::rs::alloc::string::String>, + rs_std::Option, U>) +inline constexpr rs_std::Result, + ::rs::alloc::string::String>& +rs_std::Result, + ::rs::alloc::string::String>::operator=(U&& ok) noexcept { + base_type::operator=(::std::forward(ok)); + return *this; +} +template + requires( + rs_std::ResultUnexpectedConstructible<::rs::alloc::string::String, F>) +inline constexpr rs_std::Result< + rs_std::Option, + ::rs::alloc::string::String>::Result(rs_std::unexpected&& err) noexcept + : base_type(::std::move(err)) {} +template + requires( + rs_std::ResultUnexpectedConstructible<::rs::alloc::string::String, F>) +inline constexpr rs_std::Result, + ::rs::alloc::string::String>& +rs_std::Result, ::rs::alloc::string::String>:: +operator=(rs_std::unexpected&& err) noexcept { + base_type::operator=(::std::move(err)); + return *this; +} +template +inline constexpr rs_std:: + Result, ::rs::alloc::string::String>::Result( + ::std::in_place_t ip, Args&&... args) noexcept + : base_type(ip, ::std::forward(args)...) {} +template +inline constexpr rs_std:: + Result, ::rs::alloc::string::String>::Result( + rs_std::unexpect_t u, Args&&... args) noexcept + : base_type(u, ::std::forward(args)...) {} + +#endif + +#pragma clang diagnostic pop +#endif // THIRD_PARTY_CRUBIT_EXAMPLES_RUST_STD_EXAMPLE_CRATE_GOLDEN diff --git a/examples/rust/std/main.cc b/examples/rust/std/main.cc new file mode 100644 index 000000000..ebcb013f0 --- /dev/null +++ b/examples/rust/std/main.cc @@ -0,0 +1,25 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +// The generated bindings are in a header at the same path as the +// `example_crate` rust_library, with a `.h` suffix. +#include "crubit/support/rs_std/str_ref.h" +#include "examples/rust/std/example_crate.h" +#include "support/rs_std/rs_alloc.h" + +int main(int argc, char* argv[]) { + rs_std::Result, ::rs::alloc::string::String> + result = example_crate::returns_result(true); + if (result.has_value()) { + if (result->has_value()) { + std::cout << "ok: " << **result << std::endl; + } else { + std::cout << "ok: None" << std::endl; + } + } else { + std::cout << "err:" << result.error() << std::endl; + } +}