thin_trait_object/lib.rs
1//! One pointer wide trait objects which are also FFI safe, allowing traits to be passed to/from and implemented by C ABI code.
2//!
3//! # Overview
4//! Trait objects in Rust suffer from several fundamental limitations:
5//! - **Pointers have twice the size** because trait objects are constructed with a pointer coercion rather than a value transformation — this means that the [virtual dispatch table] or a pointer to one cannot be stored inside the object and has to accompany pointers to that object, increasing size overhead for the pointers, especially for collections like `Vec<Box<dyn ...>>`;
6//! - **Passing trait objects over an FFI boundary is impossible** because they do not have a defined memory layout and implementation;
7//! - **No way to manually construct a trait object given only a dispatch table and a value**, i.e. to create a custom implementation which does not correspond to any type's implementation of the trait.
8//!
9//! For most purposes, those limitations are relatively easy to work around or are not applicable at all. However, in several scenarios, there is no possible solution and that is inherent to the nature of how trait objects work in Rust. Examples include:
10//! - **Implementing a plugin system** where plugins residing inside dynamically loaded libraries (`.dll`/`.so`/`.dylib`) can be loaded by Rust code and then be used to extend the functionality of the base program using a defined interface;
11//! - **Decreasing storage overhead for references/boxes/pointers** to trait objects, as in the `Vec<Box<dyn ...>>` example;
12//! - **Implementing traits via JIT compilation of a different language**, though this is a very niche scenario.
13//!
14//! All those workloads fit the *pattern* of trait objects but don't fit the *implementation*. This crate serves as an alternate *implementation* of trait objects which serves the *pattern* while overcoming *limitations* of the compiler's built-in implementation. The functionality is provided in the form of an easy-to-use atttribute macro.
15//!
16//! The macro was **heavily** inspired by the design and implementation of an FFI-safe trait object described in the [*FFI-Safe Polymorphism: Thin Trait Objects*] article by Michael-F-Bryan. The article is a walkthrough for writing such a trait object manually, and this crate serves as the macro to perform the same task in an automated fashion.
17//!
18//! # Usage
19//! The most basic use case:
20//! ```rust
21//! use thin_trait_object::*;
22//!
23//! #[thin_trait_object]
24//! trait Foo {
25//! fn fooify(&self);
26//! }
27//! impl Foo for String {
28//! fn fooify(&self) {
29//! println!("Fooified a string: {}", self);
30//! }
31//! }
32//! BoxedFoo::new("Hello World!".to_string()).fooify();
33//! ```
34//! The macro will generate two structures (there's a third one but that's an implementation detail):
35//! - **`FooVtable`**, the dispatch table (vtable) — a `#[repr(C)]` structure containing type-erased function pointer equivalents to all methods in the trait, as well as an additional `drop` function pointer called by `BoxedFoo` when it gets dropped (another attribute, `#[derive(Copy, Clone, Debug, Hash)]`, is added by default);
36//! - **`BoxedFoo`**, analogous to `Box<dyn Foo>` in that it acts as a valid implementation of the `Foo` trait and has exclusive ownership of the contained value, which has the same memory layout as a [`core::ptr::NonNull`] to a type which implements `Sized`.
37//!
38//! Both of those will have the same visibility modifier as the trait on which the `#[thin_trait_object]` attribute is placed, unless you override it — the section up ahead is there to explain how.
39//!
40//! ## Configuring the macro
41//! The basic invocation form, `#[thin_trait_object]`, will use the reasonable defaults for all possible configuration values. To override those configuration parameters, the following syntax is used:
42//! ```rust
43//! # /*
44//! #[thin_trait_object(
45//! parameter1(value_for_the_parameter),
46//! parameter2(another_value),
47//! // Certain parameters require a slightly different syntax, like this:
48//! parameter3 = value,
49//! )]
50//! trait Foo {
51//! ...
52//! }
53//! # */
54//! ```
55//! The following options are supported:
56//! - `vtable(<attributes> <visibility> <name>)` — specifies the visibility and name of the generated vtable structure and optionally attaches attributes to it *(that includes documentation comments)*.
57//!
58//! By default, `#[repr(C)]` and `#[derive(Copy, Clone, Debug, Hash)]` are attached, the visibility is taken from the trait definition, and the name is of form `<trait_name>Vtable`, as in `MyTraitVtable`.
59//!
60//! `#[repr(C)]` will be overriden, while the `#[derive(...)]` will not be, meaning that specifying `#[derive(PartialEq)]`, for example, will add `PartialEq` to the list of traits being derived without overriding it.
61//!
62//! Example:
63//! ```no_run
64//! # use thin_trait_object::*;
65//! #[thin_trait_object(
66//! vtable(
67//! /// Documentation for my generated vtable.
68//! # /*
69//! #[repr(custom_repr)] // Will override the default #[repr(C)]
70//! #[another_fancy_attribute]
71//! # */
72//! pub MyVtableName // No semicolon allowed!
73//! )
74//! )]
75//! # trait MyTrait {}
76//! ```
77//!
78//! - `trait_object(<attributes> <visibility> <name>)` — same as `vtable(...)`, but applies its effects to the generated boxed trait object structure.
79//!
80//! **Cannot attach a `#[derive(...)]` attribute for soundness reasons** (so that a `#[derive(Copy)]` wouldn't lead to undefined behavior without any usage of the `unsafe` keyword on the macro usage site.)
81//!
82//! By default, `#[repr(transparent)]` is attached (cannot be overriden), the visibility is taken from the trait definition, and the name is of form `Boxed<trait_name>`, as in `BoxedMyTrait`.
83//!
84//! - `inline_vtable = <true/false>` — specifies whether the vtable should be stored directly in the trait object (`true`) or be stored as a `&'static` reference to the vtable. Set to `false` by default, and **overriding this is not recommended** unless the trait has very few (one or two) methods, or it is absolutely necessary to override this in order to be compatible with certain third-party code.
85//!
86//! Example:
87//! ```rust
88//! # use thin_trait_object::*;
89//! #[thin_trait_object(
90//! inline_vtable = true
91//! )]
92//! # trait MyTrait {}
93//! ```
94//!
95//! - `drop_abi = "..."` — specifies the ABI (the `"C"` in `extern "C"`) for the `drop` function pointer in the vtable. The ABI for all other methods in the vtable can be specified in the trait definition directly.
96//!
97//! Example:
98//! ```rust
99//! # use thin_trait_object::*;
100//! #[thin_trait_object(
101//! drop_abi = "C" // Equivalent to extern "C" on a function/method
102//! )]
103//! # trait MyTrait {}
104//! ```
105//! - `marker_traits(...)` — specifies a comma-separated list of traits which are to be considered marker traits, i.e. be implemented via an empty `impl` block on the generated thin trait object structure if the trait definition lists them as supertraits. Unsafe traits in the list need to be prefixed with the `unsafe` keyword.
106//!
107//! By default, the list is `marker_traits(unsafe Send, unsafe Sync, UnwindSafe, RefUnwindSafe)`.
108//!
109//! See the [Supertraits](#supertraits) section for more on how the macro interacts with supertraits.
110//!
111//! Example:
112//! ```rust
113//! # use thin_trait_object::*;
114//! trait SafeTrait {}
115//! unsafe trait UnsafeTrait {}
116//!
117//! #[thin_trait_object(
118//! marker_traits(
119//! SafeTrait,
120//! // `unsafe` keyword here ensures that "unsafe code" is required
121//! // to produce UB by implementing the trait
122//! unsafe UnsafeTrait,
123//! )
124//! )]
125//! trait MyTrait: SafeTrait + UnsafeTrait {}
126//! ```
127//! - `store_layout = <true/false>` — specifies whether the generated vtable should also contain the `size` and `align` fields, storing the size of the stored type and its preferred alignment respectively. Set to `false` by default for compatibility.
128//!
129//! Example:
130//! ```rust
131//! # use thin_trait_object::*;
132//! #[thin_trait_object(
133//! store_layout = true
134//! )]
135//! # trait MyTrait {}
136//! ```
137//!
138//! ## Use with FFI
139//! One of the main focuses of the macro is FFI, which is why usage of the macro with FFI is simple and natural:
140//! ```no_run
141//! use thin_trait_object::*;
142//! use std::ffi::c_void;
143//!
144//! #[thin_trait_object(drop_abi = "C")]
145//! trait Foo {
146//! extern "C" fn say_hello(&self);
147//! }
148//!
149//! impl Foo for String {
150//! extern "C" fn say_hello(&self) {
151//! println!("Hello from \"{}\"", self);
152//! }
153//! }
154//!
155//! # /*
156//! extern "C" {
157//! fn eater_of_foo(foo: *mut c_void);
158//! fn creator_of_foo() -> *mut c_void;
159//! }
160//! # */
161//! # unsafe extern "C" fn eater_of_foo(_foo: *mut c_void) {}
162//! # unsafe extern "C" fn creator_of_foo() -> *mut c_void {
163//! # BoxedFoo::new("Rust pretending to be C".to_string()).into_raw() as *mut _
164//! # }
165//!
166//! let foo = BoxedFoo::new("Hello World!".to_string());
167//!
168//! unsafe {
169//! // Will transfer ownership to the C side.
170//! eater_of_foo(foo.into_raw() as *mut c_void);
171//! }
172//! // Acquire ownership of a different implementation from the C side.
173//! let foo = unsafe { BoxedFoo::from_raw(creator_of_foo() as *mut ()) };
174//! foo.say_hello();
175//! ```
176//! The C side would do:
177//! ```c
178//! #include <stdio.h>
179//!
180//! typedef void (*vtable_say_hello)(void*);
181//! typedef void (*vtable_drop)(void*);
182//! typedef struct foo_vtable {
183//! vtable_say_hello say_hello;
184//! vtable_drop drop;
185//! } foo_vtable;
186//!
187//! void eater_of_foo(void* foo) {
188//! // The first field is a pointer to the vtable, so we have to first
189//! // extract that pointer and then dereference the function pointers.
190//! foo_vtable* vtable = *((foo_vtable**)foo);
191//!
192//! // Have to provide the pointer twice, firstly for
193//! // lookup and then to provide the &self reference.
194//! vtable.say_hello(foo);
195//! // Don't forget about manual memory management — the C side owns the trait object now.
196//! vtable.drop(foo);
197//! }
198//! void* creator_of_foo(void) {
199//! // Allocate space for one pointer, the pointer to the vtable.
200//! void* allocation = malloc(sizeof(foo_vtable*));
201//! void* vtable_pointer = &custom_vtable;
202//! // Put the pointer into the allocation.
203//! memcpy(allocation, &vtable_pointer, sizeof(foo_vtable*));
204//! return allocation;
205//! }
206//!
207//! static foo_vtable custom_vtable {
208//! // Using C11 designated initializers, consult your local C expert for
209//! // ways to do this on an old compiler.
210//! .say_hello = &impl_say_hello,
211//! .drop = &impl_drop
212//! };
213//! void impl_say_hello(void* self) {
214//! puts("Hello from C!");
215//! }
216//! void impl_drop(void* self) {
217//! free(self);
218//! }
219//! ```
220//!
221//! ## Supertraits
222//! Consider this situation:
223//! ```compile_fail
224//! use thin_trait_object::*;
225//!
226//! trait A {
227//! fn a(&self);
228//! }
229//! #[thin_trait_object]
230//! trait B: A {
231//! fn b(&self);
232//! }
233//! ```
234//! This will fail to compile because the macro will try to implement `B` for `BoxedB`, the generated thin trait object structure, which will fail because `BoxedB` doesn't implement `A`. To fix this, that must be done manually:
235//! ```no_run
236//! # use thin_trait_object::*;
237//! # trait A {
238//! # fn a(&self);
239//! # }
240//! #[thin_trait_object]
241//! trait B: A {
242//! fn b(&self);
243//! #[doc(hidden)]
244//! fn _thunk_a(&self) {
245//! self.a(); // Redirect to the method from the A trait implementation
246//! }
247//! }
248//! impl A for BoxedB<'_> {
249//! fn a(&self) {
250//! // Redirect to the hidden thunk, which will use the actual implementation of the method
251//! self._thunk_a();
252//! }
253//! }
254//! ```
255//! This is necessary because the macro has no access to `A` and thus doesn't know that it needs to add its methods to the vtable.
256//! A little hacky, but there is no cleaner way of doing this using only procedural macros. If you have any suggestions for improving this pattern, raise an issue explaining your proposed solution or create a PR.
257//!
258//! ## Output reference
259//! The following is a comprehensive list of everything the macro emits:
260//! - **The trait itself**, with all other attributes.
261//! - **A virtual dispatch table struct definition.**
262//!
263//! The name can be customized via the `vtable(...)` configuration option (see the *Configuring the macro* section); the default name is `{trait name}Vtable`, as in, `FooVtable` for a trait named `Foo`.
264//!
265//! The virtual dispatch table is defined as follows:
266//! ```no_run
267//! # /*
268//! #[repr(C)] // Can be customized via configuration options
269//! #[derive(Copy, Clone, Debug, Hash)]
270//! struct FooVtable {
271//! // One field for every method in the trait
272//! drop: unsafe fn(::core::ffi::c_void), // ABI can be customized via configuration options
273//! }
274//! # */
275//! ```
276//! The other fields, ones besides `drop`, each have the same name as their corresponding trait method. The signatures are nearly identical, with two differences:
277//! - `&self` or `&mut self`, if present, are replaced with [`*mut ::core::ffi::c_void`][`core::ffi::c_void`];
278//! - If there was no `unsafe` on the trait method, it is added automatically, since the pointer passed as the first argument is never validated.
279//! - **A thin trait object struct definition.**
280//!
281//! The name can be customized via the `trait_object(...)` configuration option (see the *Configuring the macro* section); the default name is `Boxed{trait name}`, as in, `BoxedFoo` for a trait named `Foo`.
282//!
283//! The virtual dispatch table is defined as follows:
284//! ```rust
285//! # /*
286//! #[repr(transparent)]
287//! struct BoxedFoo<'inner>(
288//! ::core::ptr::NonNull<{vtable name}>,
289//! ::core::marker::PhantomData<&'inner ()>,
290//! );
291//! # */
292//! ```
293//! If the trait has a `'static` lifetime bound, the `'inner` lifetime parameter is not emitted, since all possible contained implementations are restricted to be `'static`.
294//!
295//! The following methods and associated functions are present on the boxed thin trait object structure:
296//! - ```no_run
297//! # /*
298//! fn new<T: {trait name} + Sized + 'inner>(val: T) -> Self
299//! # */
300//! ```
301//! Constructs a boxed thin trait object from a type implementing the trait. The `'inner` bound is replaced with `'static` if the `'static` lifetime is one of the supertraits on the base trait.
302//! - ```no_run
303//! # /*
304//! const unsafe fn from_raw(ptr: *mut ()) -> Self
305//! # */
306//! ```
307//! Creates a thin trait object directly from a raw pointer to its vtable.
308//!
309//! ### Safety
310//! This constructor, by its nature, is hugely unsafe and should be avoided when possible. The following invariants must be upheld:
311//! - The pointer must not be null and must point to a valid thin trait object as expected by its vtable which is not uninitialized;
312//! - The function pointers in the vtable must not be null and must point to valid functions with correct ABI and signature;
313//! - The function pointers must have the same safety contract as implied and not a stronger one: only cause UB if the vtable pointer passed to them is invalid or, if those are unsafe in the trait itself, cause UB if the safety contract in their declarations is violated;
314//! - If the trait is unsafe, the function pointers must follow the trait's contract for valid implementations;
315//! - The pointer was not returned by `as_raw` which was called on an object which was not put into [`ManuallyDrop`] or consumed by [`mem::forget`], otherwise undefined behavior will be invoked when both are dropped.
316//! - ```no_run
317//! # /*
318//! const fn as_raw(&self) -> *mut ()
319//! # */
320//! ```
321//! Extracts the contained pointer to the trait object.
322//!
323//! Unlike `into_raw`, ownership of the pointer is not released, and as such will be dropped normally. Unless the original copy is removed via [`mem::forget`] or [`ManuallyDrop`], calling `from_raw` and then dropping will cause undefined behavior.
324//! - ```no_run
325//! # /*
326//! fn into_raw(self) -> *mut ()
327//! # */
328//! ```
329//! Releases ownership of the trait object, returning the contained pointer. It is the caller's responsibility to drop the trait object at a later time using `from_raw`.
330//!
331//! For a version which does not release ownership, see `as_raw`.
332//! - ```no_run
333//! # /*
334//! fn vtable(&self) -> &{vtable name}
335//! # */
336//! ```
337//! Retrieves the raw vtable of the contained trait object.
338//!
339//! [*FFI-Safe Polymorphism: Thin Trait Objects*]: https://adventures.michaelfbryan.com/posts/ffi-safe-polymorphism-in-rust/ " "
340//! [virtual dispatch table]: https://en.wikipedia.org/wiki/Virtual_method_table " "
341//! [`core::ptr::NonNull`]: https://doc.rust-lang.org/std/ptr/struct.NonNull.html " "
342//! [`core::ffi::c_void`]: https://doc.rust-lang.org/std/ffi/enum.c_void.html " "
343//! [`ManuallyDrop`]: https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html " "
344//! [`mem::forget`]: https://doc.rust-lang.org/std/mem/fn.forget.html " "
345
346#![deny(rust_2018_idioms)]
347#![warn(missing_docs, clippy::cargo)]
348
349use proc_macro::TokenStream;
350
351/// Creates a thin trait object interface for a trait.
352#[proc_macro_attribute]
353pub fn thin_trait_object(attr: TokenStream, mut item: TokenStream) -> TokenStream {
354 let output: TokenStream = attribute_main(attr.into(), item.clone().into())
355 .unwrap_or_else(|error| error.to_compile_error())
356 .into();
357 // Concatenate the original trait definition and the generated additions
358 // only here, for three reasons:
359 // • This allows us to have both the trait definition and the custom
360 // compile error we produce, otherwise rustc itself will generate a
361 // massive amount of name resolution errors because the trait itself
362 // wouldn't exist if the macro fails
363 // • This looks neater, since we separate the logic for generating the FFI
364 // support for the trait from the logic of interfacing with the compiler,
365 // and not discarding input is a part of the compiler interface logic.
366 // • We can write concise unit tests which check the output of
367 // attribute_main (since only proc_macro2 is available in tests) and
368 // don't have to skip over the trait definition.
369 item.extend(Some(output));
370 item
371}
372
373#[macro_use]
374pub(crate) mod util {
375 macro_rules! define_path {
376 (::, $($segment:literal),+) => {{
377 ::syn::Path {
378 leading_colon: Some(Default::default()),
379 segments: define_path_segments!($($segment),+),
380 }
381 }};
382 (::, $($segment:expr),+) => {{
383 ::syn::Path {
384 leading_colon: Some(Default::default()),
385 segments: define_path_segments!($($segment),+),
386 }
387 }};
388 ($($segment:literal),+) => {{
389 ::syn::Path {
390 leading_colon: None,
391 segments: define_path_segments!($($segment),+),
392 }
393 }};
394 ($($segment:expr),+) => {{
395 ::syn::Path {
396 leading_colon: None,
397 segments: define_path_segments!($($segment),+),
398 }
399 }};
400 }
401 macro_rules! define_path_segments {
402 ($($segment:literal),+) => {{
403 let mut segments = ::syn::punctuated::Punctuated::new();
404 $(
405 segments.push(::syn::PathSegment {
406 ident: ::syn::Ident::new($segment, ::proc_macro2::Span::call_site()),
407 arguments: ::syn::PathArguments::None,
408 });
409 )+
410 segments
411 }};
412 ($($segment:expr),+) => {{
413 let mut segments = ::syn::punctuated::Punctuated::new();
414 $(
415 segments.push(::syn::PathSegment {
416 ident: $segment,
417 arguments: ::syn::PathArguments::None,
418 });
419 )+
420 segments
421 }};
422 }
423}
424
425mod attr;
426use attr::*;
427pub(crate) mod marker_traits;
428pub(crate) mod options;
429pub(crate) mod repr;
430pub(crate) mod trait_object;
431pub(crate) mod vtable;
432
433/// Convinces [`cargo geiger`] that the crate has unsafe code.
434///
435/// Since we only generate unsafe code rather than call it directly, `cargo geiger` won't spot that and will report that it couldn't find any unsafe code (though not display the green `#![forbid(unsafe_code)]` smiley because we don't have `#![forbid(unsafe_code)]`).
436///
437/// To make it clear for users that the crate uses unsafe code in some form and thus is subject to security auditing, we have this unsafe function which is never called and will not do anything. This will immediately be unconditionally found by `cargo geiger` and reported as a form of unsafe code being used.
438///
439/// [`cargo geiger`]: https://crates.io/crates/cargo-geiger " "
440unsafe fn _dummy_unsafe() {}