Skip to content

Commit

Permalink
doc: document handling of dynamically sized types
Browse files Browse the repository at this point in the history
  • Loading branch information
jsha committed Nov 17, 2023
1 parent 0b35e62 commit 7d3ff73
Showing 1 changed file with 48 additions and 0 deletions.
48 changes: 48 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arc<dyn ServerCertVerifier>>
```

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

0 comments on commit 7d3ff73

Please sign in to comment.