Skip to content

Commit be22052

Browse files
thunderseethecopybara-github
authored andcommitted
Add docs for trait bindings.
PiperOrigin-RevId: 873122996
1 parent d68f26c commit be22052

File tree

6 files changed

+330
-3
lines changed

6 files changed

+330
-3
lines changed

docs/rust/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ prefer just copy-pasting something, start there.
1313
## How to use Crubit {#introduction}
1414

1515
Crubit allows you to call some Rust interfaces from C++. It supports
16-
[functions](functions.md) (including methods), [structs](structs.md), and even
17-
[enums](enums.md) as "opaque" objects. Crubit does **not** support advanced
18-
features like generics or dynamic dispatch with `dyn`.
16+
[functions](functions.md) (including methods), [structs](structs.md),
17+
[traits](traits.md), and even [enums](enums.md) as "opaque" objects. Crubit does
18+
**not** support advanced features like generics or dynamic dispatch with `dyn`.
1919

2020
The rest of this document goes over how to create a Rust library that can be
2121
called from C++, and how to actually use it from C++. The quick summary is:

docs/rust/traits.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# C++ bindings for Rust traits
2+
3+
Rust traits and trait implementations are mapped into a template struct and
4+
template struct specialization respectively.
5+
6+
Today only a subset of trait functionality is supported:
7+
8+
* Generic traits are not supported.
9+
* Blanket Implementations are not supported.
10+
* Only associated functions of the trait receive bindings today.
11+
* The same constraints required for a top-level function to receive bindings
12+
apply to trait functions (each parameter and return type receives bindings
13+
etc.).
14+
15+
## Example.
16+
17+
Given the following Rust crate:
18+
19+
```
20+
{{ #include ../../examples/rust/trait/example.rs }}
21+
```
22+
<!-- -->
23+
24+
25+
Crubit will generate three bindings. A template struct for `MyTrait`:
26+
27+
```
28+
{{ #include ../../examples/rust/trait/example_generated.h }}
29+
```
30+
<!-- class:MyTrait -->
31+
32+
33+
A struct for `MyStruct`:
34+
35+
```
36+
{{ #include ../../examples/rust/trait/example_generated.h }}
37+
```
38+
<!-- class:MyStruct -->
39+
40+
41+
A template specialization for `impl MyTrait for MyStruct`:
42+
43+
```
44+
{{ #include ../../examples/rust/trait/example_generated.h }}
45+
```
46+
<!-- class:impl -->
47+
48+
49+
We can see an example of how we call our generated trait methods:
50+
51+
```
52+
{{ #include ../../examples/rust/trait/main.cc }}
53+
```
54+
<!-- function:main -->
55+
56+
57+
Each bound trait provides its implementations via the `impl` member, which
58+
selects the generated specialization (if one exists). We can call our trait
59+
method using the `Trait::impl<Args...>::trait_method(args...)` syntax mirroring
60+
Rust's fully qualified method dispatch.
61+
62+
## What about when a trait isn't implemented?
63+
64+
Our example so far, rather conveniently, only calls trait implementations that
65+
exist. But what about when a trait implementation doesn't exist? We're just as
66+
free to write `MyTrait::impl<uint32_t>::add_with(...)` despite there being no
67+
`impl MyTrait for i32` in Rust.
68+
69+
In such a situation, we generate a fresh instantiation of our `MyTrait` template
70+
devoid of any methods. This will cause a compilation error. If we're writing our
71+
own templated code and we want to check that our a template argument `T`
72+
implements a trait, we can use `rs_std::where`.
73+
74+
We can write a templated function that requires our trait implementation:
75+
76+
```cpp
77+
template <typename T>
78+
requires(rs_std::where_v<T, example_crate::MyTrait>)
79+
uint32_t add_with_2(T const& self) {
80+
return example_crate::MyTrait::impl<T>::add_with(self, 2);
81+
}
82+
```
83+
84+
A constexpr expression is also available as `rs_std::where_v`.

examples/rust/trait/BUILD

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
2+
load(
3+
"@rules_rust//rust:defs.bzl",
4+
"rust_library",
5+
)
6+
load(
7+
"//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
8+
"cc_bindings_from_rust",
9+
)
10+
load(
11+
"//cc_bindings_from_rs/test/golden:golden_test.bzl",
12+
"golden_test",
13+
)
14+
15+
package(default_applicable_licenses = ["//:license"])
16+
17+
licenses(["notice"])
18+
19+
# This declares an "example_crate_cc_api" target that provides Crubit-generated
20+
# C++ bindings for the Rust crate behind the `":example_crate"` target.
21+
rust_library(
22+
name = "example_crate",
23+
srcs = ["example.rs"],
24+
)
25+
26+
cc_bindings_from_rust(
27+
name = "example_crate_cc_api",
28+
crate = ":example_crate",
29+
)
30+
31+
cc_binary(
32+
name = "main",
33+
srcs = ["main.cc"],
34+
deps = [
35+
":example_crate_cc_api",
36+
"//support/rs_std:str_ref",
37+
],
38+
)
39+
40+
golden_test(
41+
name = "example_golden_test",
42+
golden_h = "example_generated.h",
43+
rust_library = "example_crate",
44+
)

examples/rust/trait/example.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Part of the Crubit project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
pub trait MyTrait {
6+
fn add_with(&self, y: i32) -> i32;
7+
8+
fn describe(&self) -> &'static str;
9+
}
10+
11+
#[derive(Clone, Copy, Default)]
12+
pub struct MyStruct {
13+
x: i32,
14+
}
15+
16+
impl MyStruct {
17+
pub fn new(x: i32) -> Self {
18+
Self { x }
19+
}
20+
}
21+
22+
impl MyTrait for MyStruct {
23+
fn add_with(&self, y: i32) -> i32 {
24+
self.x + y
25+
}
26+
27+
fn describe(&self) -> &'static str {
28+
"MyStruct"
29+
}
30+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Part of the Crubit project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
// Automatically @generated C++ bindings for the following Rust crate:
6+
// example_crate_golden
7+
// Features: custom_ffi_types, non_unpin_ctor, std_unique_ptr, std_vector,
8+
// supported
9+
10+
// clang-format off
11+
#ifndef THIRD_PARTY_CRUBIT_EXAMPLES_RUST_TRAIT_EXAMPLE_CRATE_GOLDEN
12+
#define THIRD_PARTY_CRUBIT_EXAMPLES_RUST_TRAIT_EXAMPLE_CRATE_GOLDEN
13+
14+
#pragma clang diagnostic push
15+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
16+
#include "support/annotations_internal.h"
17+
#include "support/internal/slot.h"
18+
#include "support/rs_std/str_ref.h"
19+
#include "support/rs_std/traits.h"
20+
21+
#include <cstddef>
22+
#include <cstdint>
23+
#include <type_traits>
24+
#include <utility>
25+
26+
namespace example_crate {
27+
28+
// Generated from:
29+
// examples/rust/trait/example.rs;l=12
30+
struct CRUBIT_INTERNAL_RUST_TYPE(":: example_crate_golden :: MyStruct") alignas(
31+
4) [[clang::trivial_abi]] MyStruct final {
32+
public:
33+
// Default::default
34+
MyStruct();
35+
36+
// No custom `Drop` impl and no custom "drop glue" required
37+
~MyStruct() = default;
38+
MyStruct(MyStruct&&) = default;
39+
::example_crate::MyStruct& operator=(MyStruct&&) = default;
40+
41+
// Rust types that are `Copy` get trivial, `default` C++ copy constructor and
42+
// assignment operator.
43+
MyStruct(const MyStruct&) = default;
44+
::example_crate::MyStruct& operator=(const MyStruct&) = default;
45+
MyStruct(::crubit::UnsafeRelocateTag, MyStruct&& value) {
46+
memcpy(this, &value, sizeof(value));
47+
}
48+
49+
// Generated from:
50+
// examples/rust/trait/example.rs;l=17
51+
static ::example_crate::MyStruct new_(std::int32_t x);
52+
53+
private:
54+
union {
55+
// Generated from:
56+
// examples/rust/trait/example.rs;l=13
57+
std::int32_t x;
58+
};
59+
60+
private:
61+
static void __crubit_field_offset_assertions();
62+
};
63+
64+
// Generated from: examples/rust/trait/example.rs;l=5
65+
struct CRUBIT_INTERNAL_RUST_TYPE(":: example_crate_golden :: MyTrait") MyTrait {
66+
template <typename T>
67+
using impl = rs_std::impl<T, MyTrait>;
68+
};
69+
70+
static_assert(
71+
sizeof(MyStruct) == 4,
72+
"Verify that ADT layout didn't change since this header got generated");
73+
static_assert(
74+
alignof(MyStruct) == 4,
75+
"Verify that ADT layout didn't change since this header got generated");
76+
namespace __crubit_internal {
77+
extern "C" void __crubit_thunk_default(::example_crate::MyStruct* __ret_ptr);
78+
}
79+
inline ::example_crate::MyStruct::MyStruct() {
80+
__crubit_internal::__crubit_thunk_default(this);
81+
}
82+
static_assert(std::is_trivially_destructible_v<MyStruct>);
83+
static_assert(
84+
std::is_trivially_move_constructible_v<::example_crate::MyStruct>);
85+
static_assert(std::is_trivially_move_assignable_v<::example_crate::MyStruct>);
86+
static_assert(
87+
std::is_trivially_copy_constructible_v<::example_crate::MyStruct>);
88+
static_assert(std::is_trivially_copy_assignable_v<::example_crate::MyStruct>);
89+
namespace __crubit_internal {
90+
extern "C" void __crubit_thunk_new(std::int32_t,
91+
::example_crate::MyStruct* __ret_ptr);
92+
}
93+
inline ::example_crate::MyStruct MyStruct::new_(std::int32_t x) {
94+
crubit::Slot<::example_crate::MyStruct> __return_value_ret_val_holder;
95+
auto* __return_value_storage = __return_value_ret_val_holder.Get();
96+
__crubit_internal::__crubit_thunk_new(x, __return_value_storage);
97+
return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue();
98+
}
99+
inline void MyStruct::__crubit_field_offset_assertions() {
100+
static_assert(0 == offsetof(MyStruct, x));
101+
}
102+
} // namespace example_crate
103+
104+
template <>
105+
struct rs_std::impl<::example_crate::MyStruct, ::example_crate::MyTrait> {
106+
static constexpr bool kIsImplemented = true;
107+
108+
// Generated from:
109+
// examples/rust/trait/example.rs;l=23
110+
static std::int32_t add_with(::example_crate::MyStruct const& self,
111+
std::int32_t y);
112+
113+
// Generated from:
114+
// examples/rust/trait/example.rs;l=27
115+
static rs_std::StrRef describe(::example_crate::MyStruct const& self);
116+
};
117+
118+
namespace example_crate {
119+
namespace __crubit_internal {
120+
extern "C" std::int32_t __crubit_thunk_MyTrait_uadd_uwith(
121+
::example_crate::MyStruct const&, std::int32_t);
122+
}
123+
} // namespace example_crate
124+
inline std::int32_t
125+
rs_std::impl<::example_crate::MyStruct, ::example_crate::MyTrait>::add_with(
126+
::example_crate::MyStruct const& self, std::int32_t y) {
127+
return example_crate::__crubit_internal::__crubit_thunk_MyTrait_uadd_uwith(
128+
self, y);
129+
}
130+
131+
namespace example_crate {
132+
namespace __crubit_internal {
133+
extern "C" rs_std::StrRef __crubit_thunk_MyTrait_udescribe(
134+
::example_crate::MyStruct const&);
135+
}
136+
} // namespace example_crate
137+
inline rs_std::StrRef
138+
rs_std::impl<::example_crate::MyStruct, ::example_crate::MyTrait>::describe(
139+
::example_crate::MyStruct const& self) {
140+
return example_crate::__crubit_internal::__crubit_thunk_MyTrait_udescribe(
141+
self);
142+
}
143+
144+
#pragma clang diagnostic pop
145+
#endif // THIRD_PARTY_CRUBIT_EXAMPLES_RUST_TRAIT_EXAMPLE_CRATE_GOLDEN

examples/rust/trait/main.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Part of the Crubit project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include <cstdint>
6+
#include <iostream>
7+
8+
#include "support/rs_std/str_ref.h"
9+
// The generated bindings are in a header at the same path as the
10+
// `example_crate` rust_library, with a `.h` suffix.
11+
#include "examples/rust/trait/example_crate.h"
12+
13+
int main(int argc, char* argv[]) {
14+
// The generated bindings are in a namespace with the same name as the
15+
// target crate:
16+
example_crate::MyStruct s = example_crate::MyStruct::new_(2);
17+
uint32_t result =
18+
example_crate::MyTrait::impl<example_crate::MyStruct>::add_with(s, 3);
19+
rs_std::StrRef description =
20+
example_crate::MyTrait::impl<example_crate::MyStruct>::describe(s);
21+
std::cout << "Result: " << result
22+
<< "\ndescription: " << description.to_string_view() << std::endl;
23+
return 0;
24+
}

0 commit comments

Comments
 (0)