diff --git a/docs/rust/functions.md b/docs/rust/functions.md index 98588d710..0bb7dccc6 100644 --- a/docs/rust/functions.md +++ b/docs/rust/functions.md @@ -82,6 +82,31 @@ 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]`, `&str`) map to `rs_std::SliceRef` and +`rs_std::StrRef` in C++. + +C++ callers can pass string literals (`"hello"`) or `std::string_view` directly +to satisfy string and byte-slice parameters. + +```rust {.good} +fn process_text(_: &str) {} // C++ callers can pass "hello" or std::string_view +fn process_bytes(_: &[u8]) {} // C++ callers can pass "hello", std::string_view, or std::span +``` + +However, mutable slices cannot be constructed from string literals: + +```rust {.bad} +fn process_mut_bytes(_: &mut [u8]) {} // Cannot pass string literals to mutable byte slices +``` + +* **`&str`**: Maps to `rs_std::StrRef` (`crubit/support/rs_std/str_ref.h`). + Converts from string literals and `std::string_view`. +* **`&[u8]`**: Maps to `rs_std::SliceRef` + (`crubit/support/rs_std/slice_ref.h`). Converts from string literals, + `std::string_view`, `std::vector`, and `std::array`. + ## Generic functions Crubit doesn't support generating bindings for *arbitrary* generic functions. diff --git a/support/rs_std/slice_ref.h b/support/rs_std/slice_ref.h index cf26b56ae..1af9c0985 100644 --- a/support/rs_std/slice_ref.h +++ b/support/rs_std/slice_ref.h @@ -11,7 +11,7 @@ #include #include #include // NOLINT(build/c++20); -#include // NOLINT(build/c++20); +#include // NOLINT(build/c++20); #include #include @@ -34,35 +34,42 @@ class CRUBIT_INTERNAL_RUST_TYPE("&[]", T) CRUBIT_TRIVIAL_ABI CRUBIT_VIEW // To mirror slices in Rust, the data pointer is not null. constexpr SliceRef() noexcept : dangling_ptr_(alignof(T)), size_(0) {} + // Primary constructor that initializes dangling_ptr_ for empty slices to + // satisfy Rust's non-null slice invariant, or a ptr_ otherwise. + constexpr SliceRef(T* ptr, size_t size) noexcept + : dangling_ptr_(alignof(T)), size_(size) { + if (size > 0 && ptr != nullptr) { + ptr_ = ptr; + } + } + // Style waiver for implicit conversions granted in cl/662479273. // NOLINTNEXTLINE(google-explicit-constructor) constexpr SliceRef(std::span span) noexcept - // Store a dangling pointer assuming `span` is empty-- we have to - // initialize the union to something. - : dangling_ptr_(alignof(T)), size_(span.size()) { - // Store a valid pointer when `span` is not empty. - if (!span.empty()) { - ptr_ = span.data(); - } - } + : SliceRef(span.data(), span.size()) {} // Implicit conversion from mutable SliceRef to const SliceRef. // NOLINTNEXTLINE(google-explicit-constructor) constexpr SliceRef(const SliceRef>& other) noexcept requires(std::is_const_v) - : SliceRef(std::span(other.data(), other.size())) {} + : SliceRef(other.data(), other.size()) {} - // Explicit conversion from `std::string_view` in order to avoid - // marking this case as `CRUBIT_LIFETIME_BOUND`. + // Conversion from `std::string_view` for 1-byte const types. + // We explicitly overload to avoid marking this case as CRUBIT_LIFETIME_BOUND. // // Note that `std::span` solves this using an `EnableIfIsView` typeclass. // // Style waiver for implicit conversions granted in cl/662479273. // NOLINTNEXTLINE(google-explicit-constructor) constexpr SliceRef(std::string_view str) noexcept + requires(std::is_const_v && sizeof(T) == 1) : dangling_ptr_(alignof(T)), size_(str.size()) { if (!str.empty()) { - ptr_ = str.data(); + if constexpr (std::is_same_v, char>) { + ptr_ = str.data(); + } else { + ptr_ = reinterpret_cast(str.data()); + } } } diff --git a/support/rs_std/slice_ref_test.cc b/support/rs_std/slice_ref_test.cc index a9d60050d..9c1d443e8 100644 --- a/support/rs_std/slice_ref_test.cc +++ b/support/rs_std/slice_ref_test.cc @@ -203,6 +203,20 @@ TEST(ImplicitConversionTest, ToAbslSpan) { EXPECT_THAT(span, ElementsAre(1, 2)); } +TEST(ImplicitConversionTest, FromStringViewChar) { + constexpr std::string_view kStr = "hello"; + constexpr rs_std::SliceRef kSlice(kStr); + EXPECT_EQ(kSlice.size(), 5); + EXPECT_EQ(kSlice.data(), kStr.data()); +} + +TEST(ImplicitConversionTest, FromConstUint8Array) { + static constexpr std::array kArr = {'h', 'e', 'l', 'l', 'o'}; + static constexpr rs_std::SliceRef kSlice = kArr; + EXPECT_EQ(kSlice.size(), 5); + EXPECT_EQ(kSlice.to_span()[0], 'h'); +} + void Fuzzer(std::vector data) { const rs_std::SliceRef s = data; EXPECT_EQ(absl::Span(data), s.to_span());