diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c8b3d59..ed832586 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,3 +26,51 @@ If you're making changes to rustls-ffi, you'll need regenerate the header file: make src/rustls.h + +## Dynamically Sized Types + +Many types exposed in this API are wrapped in a `Box` or an `Arc`, which can be +straightforwardly turned into a raw pointer and shared with C using `into_raw`. + +However, Rust has a category of [Dynamically Sized Types] (DSTs), which in particular +includes [trait objects] (i.e. `dyn Foo`). DSTs must always be wrapped in a +pointer type, e.g. `Box`, `Arc`, `&`, `&mut`, `*mut`, or `*const`. When a pointer +type wraps a DST it's colloquially called a "fat pointer" because it's twice the +size of a pointer to a sized type. In the case of trait objects, the extra data +is a pointer to the vtable. + +Even though Rust supports raw, fat pointers, they are not FFI-safe. Consider +this example: + +```rust +extern "C" fn foo(_: *const dyn ToString) { } +``` + +``` +warning: `extern` fn uses type `dyn ToString`, which is not FFI-safe + --> foo.rs:1:22 + | +1 | extern "C" fn foo(_: *const dyn ToString) { } + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: trait objects have no C equivalent + = note: `#[warn(improper_ctypes_definitions)]` on by default +``` + +That makes sense: in the C ABI, all pointers are the same size. There is no +concept of a fat pointer. + +Since the Rustls API includes some use of trait objects, we need a way to +represent them in the C ABI. We do that by creating two pointers: an outer, +thin pointer (usually a `Box`), and an inner, fat pointer (usually an `Arc`). +For instance: + +```rust +Box> +``` + +This allows us to convert the outer pointer with `into_raw()` and pass it back +and forth across the FFI boundary. + +[Dynamically Sized Types]: https://doc.rust-lang.org/beta/reference/dynamically-sized-types.html +[trait objects]: https://doc.rust-lang.org/beta/reference/types/trait-object.html