{}
```
Unsound abstractions are never permissible. If you cannot safely encapsulate something, you must expose `unsafe` functions instead, and document proper behavior.
No Exceptions
While you may break most guidelines if you have a good enough reason, there are no exceptions in this case: unsound code is never acceptable.
> ### It's the Module Boundaries
>
> Note that soundness boundaries equal module boundaries! It is perfectly fine, in an otherwise safe abstraction,
> to have safe functions that rely on behavior guaranteed elsewhere **in the same module**.
>
> ```rust
> struct MyDevice(*const u8);
>
> impl MyDevice {
> fn new() -> Self {
> // Properly initializes instance ...
> # todo!()
> }
>
> fn get(&self) -> u8 {
> // It is perfectly fine to rely on `self.0` being valid, despite this
> // function in-and-by itself being unable to validate that.
> unsafe { *self.0 }
> }
> }
>
> ```
---
# Documentation
## Documentation has canonical sections (M-CANONICAL-DOCS) { #M-CANONICAL-DOCS }
established Rust documentation practices.
Public library items must contain the canonical doc sections. The summary sentence must always be present. Extended documentation and examples
are strongly encouraged. The other sections must be present when applicable.
```rust
/// Summary sentence < 15 words.
///
/// Extended documentation in free form.
///
/// # Examples
/// One or more examples that show API usage like so.
///
/// # Errors
/// If fn returns `Result`, list known error conditions
///
/// # Panics
/// If fn may panic, list when this may happen
///
/// # Safety
/// If fn is `unsafe` or may otherwise cause UB, this section must list
/// all conditions a caller must uphold.
///
/// # Abort
/// If fn may abort the process, list when this may happen.
pub fn foo() {}
```
In contrast to other languages, you should not create a table of parameters. Instead parameter use is explained in plain text. In other words, do not
```rust,ignore
/// Copies a file.
///
/// # Parameters
/// - src: The source.
/// - dst: The destination.
fn copy(src: File, dst: File) {}
```
but instead:
```rust,ignore
/// Copies a file from `src` to `dst`.
fn copy(src: File, dst: File) {}
```
### Related Reading
- Function docs include error, panic, and safety considerations ([C-FAILURE](https://rust-lang.github.io/api-guidelines/documentation.html#c-failure))
## Mark `pub use` items with `#[doc(inline)]` (M-DOC-INLINE) { #M-DOC-INLINE }
re-exported items that fit in with their siblings.
When publicly re-exporting crate items via `pub use foo::Foo` or `pub use foo::*`, they show up in an opaque re-export block. In most cases, this is not
helpful to the reader:

Instead, you should annotate them with `#[doc(inline)]` at the `use` site, for them to be inlined organically:
```rust,edition2021,ignore
# pub(crate) mod foo { pub struct Foo; }
#[doc(inline)]
pub use foo::*;
// or
#[doc(inline)]
pub use foo::Foo;
```

This does not apply to `std` or 3rd party types; these should always be re-exported without inlining to make it clear they are external.
> ### Still avoid glob exports
>
> The `#[doc(inline)]` trick above does not change [M-NO-GLOB-REEXPORTS]; you generally should not re-export items via wildcards.
[M-NO-GLOB-REEXPORTS]: ../libs/resilience/#M-NO-GLOB-REEXPORTS
## First sentence is one line; approx. 15 words (M-FIRST-DOC-SENTENCE) { #M-FIRST-DOC-SENTENCE }
easily skimmable API docs.
When you document your item, the first sentence becomes the "summary sentence" that is extracted and shown in the module summary:
```rust
/// This is the summary sentence, shown in the module summary.
///
/// This is other documentation. It is only shown in that item's detail view.
/// Sentences here can be as long as you like and it won't cause any issues.
fn some_item() { }
```
Since Rust API documentation is rendered with a fixed max width, there is a naturally preferred sentence length you should not
exceed to keep things tidy on most screens.
If you keep things in a line, your docs will become easily skimmable. Compare, for example, the standard library:

Otherwise, you might end up with _widows_ and a generally unpleasant reading flow:

As a rule of thumb, the first sentence should not exceed **15 words**.
## Has comprehensive module documentation (M-MODULE-DOCS) { #M-MODULE-DOCS }
easy API docs navigation.
Any public library module must have `//!` module documentation, and the first sentence must follow [M-DOC-FIRST-SENTENCE].
```rust,edition2021,ignore
pub mod ffi {
//! Contains FFI abstractions.
pub struct String {};
}
```
The rest of the module documentation should be comprehensive, i.e., cover the most relevant technical aspects of the contained items, including
- what the module contains
- when it should be used, possibly when not
- examples
- subsystem specifications (e.g., `std::fmt` [also describes its formatting language](https://doc.rust-lang.org/stable/std/fmt/index.html#formatting-parameters))
- observable side effects, including what guarantees are made about these, if any
- relevant implementation details, e.g., the used system APIs
Great examples include:
- [`std::fmt`](https://doc.rust-lang.org/stable/std/fmt/index.html)
- [`std::pin`](https://doc.rust-lang.org/stable/std/pin/index.html)
- [`std::option`](https://doc.rust-lang.org/stable/std/option/index.html)
This does not mean every module should contain all of these items. But if there is something to say about the interaction of the contained types,
their module documentation is the right place.
[M-DOC-FIRST-SENTENCE]: ./#M-DOC-FIRST-SENTENCE
---
# FFI Guidelines
## FFI crates follow established naming conventions (M-FFI-NAMING) { #M-FFI-NAMING }
immediately recognizable crate roles across projects.
Crates used for FFI should follow established naming practices:
- `-sys` for crates defining items to call into existing (C-style) libraries
- `-ffi` for crates defining (C-style) items when called from existing applications
There are slight variations of this scheme (e.g., `-sys2` when a previous `-sys` crate was abandoned and using `-` vs `_`), but overall `-ffi` clearly defines 'export' libraries, and `-sys` 'import' ones.
## Business logic belongs in core crates, FFI only translates (M-FFI-TRANSLATES) { #M-FFI-TRANSLATES }
maximal safe code and a clean separation of concerns.
When Rust is used to create FFI libraries, there should be a clear separation of concerns between the core _business logic_ crate `foo` and the glue crate `foo-ffi`.
Any operational functionality belongs in the core crate and should be expressed as idiomatic, safe, testable Rust. The FFI crate exists only to translate between native Rust and C constructs, and the core crate must not be infected with interop concerns, even if this means repeating, and slightly adjusting, type and function signatures. For example, given the following type in the core crate `foo`:
```rust,ignore
pub struct Message {
destination: [u8; 8],
data: Vec,
}
impl Message {
pub fn new(destination: [u8; 8], data: Vec) -> Self { /* ... */ }
pub fn transmit(&self) -> Result<(), TransmitError> { /* ... */ }
}
```
A proper separation of concerns might collapse construction and transmission into a single FFI entry point in `foo-ffi`:
```rust,ignore
#[no_mangle]
pub unsafe extern "C" fn transmit_message(
destination: *const [u8; 8],
data: *const u8,
data_len: usize,
) -> u8 {
let data = std::slice::from_raw_parts(data, data_len).to_vec();
match Message::new(*destination, data).transmit() {
Ok(()) => 0,
Err(_) => 1,
}
}
```
However, it would be improper to leak FFI requirements into `foo` itself: ownership, data models and signatures do not translate seamlessly between the two worlds. Any time _saved_ by skipping a clean split will have to be paid back many times over during refactorings down the line.
```rust
#[repr(C)]
pub struct Message {
pub destination: [u8; 8],
pub data_ptr: *mut u8,
pub data_len: usize,
pub data_cap: usize,
}
```
## Isolate DLL state between FFI libraries (M-ISOLATE-DLL-STATE) { #M-ISOLATE-DLL-STATE }
data integrity and defined behavior across DLL boundaries.
When loading multiple Rust-based dynamic libraries (DLLs) within one application, you may only share 'portable' state between these libraries.
Likewise, when authoring such libraries, you must only accept or provide 'portable' data from foreign DLLs.
Portable here means data that is safe and consistent to process regardless of its origin. By definition, this is a subset of FFI-safe types.
A type is portable if it is `#[repr(C)]` (or similarly well-defined), and _all_ of the following:
- It must not have any interaction with any `static` or thread local.
- It must not have any interaction with any `TypeId`.
- It must not contain any value, pointer or reference to any non-portable data (it is valid to point into portable data within non-portable data, such as
sharing a reference to an ASCII string held in a `Box`).
_Interaction_ means any computational relationship, and therefore also relates to how the type is used. Sending a `u128` between DLLs is OK, using it to
exchange a transmuted `TypeId` isn't.
The underlying issue stems from the Rust compiler treating each DLL as an entirely new compilation artifact, akin to a standalone application. This means each DLL:
- has its own set of `static` and thread-local variables,
- the type layout of any `#[repr(Rust)]` type (the default) can differ between compilations,
- has its own set of unique type IDs, differing from any other DLL.
Notably, this affects:
- ⚠️ any allocated instance, e.g., `String`, `Vec`, `Box`, ...
- ⚠️ any library relying on other statics, e.g., `tokio`, `log`,
- ⚠️ any struct not `#[repr(C)]`,
- ⚠️ any data structure relying on consistent `TypeId`.
In practice, transferring any of the above between libraries leads to data loss, state corruption, and usually undefined behavior.
Take particular note that this may also apply to types and methods that are invisible at the FFI boundary:
```rust,ignore
/// A method in DLL1 that wants to use a common service from DLL2
#[ffi_function]
fn use_common_service(common: &CommonService) {
// This has at least two issues:
// - `CommonService`, or ANY type nested deep within might have
// a different type layout in DLL2, leading to immediate
// undefined behavior (UB) ⚠️
// - `do_work()` here looks like it will be invoked in DLL2, but
// the code executed will actually come from DLL1. This means that
// `do_work()` invoked here will see a data structure coming from
// DLL2, but will use statics from DLL1 ⚠️
common.do_work();
}
```
---
# Library Guidelines
---
# Macros Guidelines
## Prefer 'macros by example' over proc macros (M-EXAMPLE-OVER-PROC) { #M-EXAMPLE-OVER-PROC }
easy macro inspection and fast compilation.
When a 'macro by example' can do the job, it should be preferred over proc macros.
Proc macros are more powerful, but their expansion can't easily be inspected. Where this versatility isn't needed, a simple 'macro by example' is the better option.
```rust,ignore
// Bad, attribute macro requires proc macro machinery, can be hard to
// inspect in some IDEs, and isn't needed here.
#[make_new_id]
struct MyId;
// Good, easier to write, maintain and inspect, faster compilation speed.
make_new_id!(MyId);
```
## Third party items come from hidden `_private` module (M-MACRO-HELPERS) { #M-MACRO-HELPERS }
predictable compilation.
When a macro expansion needs to refer to third-party items, the host crate should re-export those from a hidden module, and the macro should emit fully-qualified paths through that module rather than expecting the user's crate to depend on the third-party crate directly.
For example, a crate `foo` requiring `bar` traits would do:
```rust,ignore
#[doc(hidden)]
pub mod _private {
pub use ::bar::Bar;
}
pub use foo_proc::my_macro;
```
The `my_macro!` implementation would then rely on its presence in its emitted code:
```rust,ignore
impl ::foo::_private::Bar for MyType { ... }
```
## Macros are a last resort (M-MACRO-LAST-RESORT) { #M-MACRO-LAST-RESORT }
minimal complexity.
Macros should only be used if no other viable solution exists, compare this adage:
> As @littlecalculist always told me, “macros are for when you run out of language”. If you still have language left — and Rust gives you a lot of language — use the language first.
>
> @pcwalton
Macros are powerful, but come with several downsides. They
- are magic, and it can be impossible to predict what they do, or how they do it,
- disproportionally increase compilation time in projects that otherwise don't rely on them,
- can cause subtle breakage at edition boundaries where Rust syntax and semantics can change.
Counterintuitively, the more structurally complex the result of a macro expansion is, the worse an idea it is to use macros for that in the first place. The ideal macro makes your users go "_I know exactly what this will generate, but I don't want to write all of that by hand_".
## Macros assume main crate (M-MACRO-MAIN-CRATE) { #M-MACRO-MAIN-CRATE }
simple macro logic.
Procedural macros can (and should) assume they are used through their main crate and emit paths for that.
For crates including proc macros it is common to ship them split in 3 for technical reasons:
- `foo` - the main crate that re-exports macros from `foo_proc`, along with extra traits or types,
- `foo_proc` - facade re-exporting macros from `foo_proc_impl` with `proc-macro = true`,
- `foo_proc_impl` - the actual macro implementation and unit tests.
In some cases there can be additional crates involved. Authors might be tempted to make `foo`, `foo_proc`, and siblings all work, resulting in complex re-export hierarchies or the use of 3rd party helpers. In reality, the minimal UX gain is usually not worth the added complexity (or compile time overhead), given the ecosystem precedent of mostly not supporting these usage modes in the first place.
This also implies you should not attempt to support use cases where your crate is imported under a different name.
## Macros don't lie about signatures (M-MACROS-DONT-LIE) { #M-MACROS-DONT-LIE }
clarity for users and LLMs.
Macros must not (make users) misrepresent signatures or the shape of items.
Macros have the ability to arbitrarily rewrite token streams. They could convert structs to enums, traits to functions, or perform any other transformation imaginable. They should, however, do none of that, as the resulting code will be highly confusing and virtually impossible to predict or reason about.
Among others, macros must not
- visibly convert the nature of data types (e.g., structs to enums, ...),
- alter function signatures,
- convert the `async`-ness of items,
- do anything else that materially detaches _what's written_ from _what's happening_.
```rust,ignore
// Bad: Adds extra parameter and marks function `async`. Impossible to
// predict from reading code.
#[magic_transform]
fn foo() { }
foo(token).await
```
## Proc macros should have separate impl crate incl. tests (M-PROC-IMPL) { #M-PROC-IMPL }
thoroughly testable proc macros.
Proc macros should be thin shims inside some `foo_proc` crate that delegate to a separate, regular library crate, usually called `foo_proc_impl`, which contains the actual token-stream transformation logic and its tests.
As proc macro crates are special, testing them from `foo_proc` usually requires workarounds for unit and snapshot tests. Instead, consider having a `foo_proc_impl` crate:
```rust,ignore
use proc_macro2::TokenStream;
pub fn my_macro(attr: TokenStream, item: TokenStream) -> TokenStream { ... }
```
These can come with regular [insta](https://insta.rs/) or similar snapshot tests, and are then exported as genuine proc macros via a `foo_proc` crate like so:
```rust,ignore
#[proc_macro_attribute]
pub fn my_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
foo_proc_impl::my_macro(attr.into(), item.into()).into()
}
```
The macros are then re-exported from the core crate:
```rust,ignore
pub use foo_proc::my_macro;
```
Inside the core crate, we also recommend adding [trybuild](https://docs.rs/trybuild/latest/trybuild/) UI tests with negative examples to ensure consistent error messages.
## Proc macros don't produce implied or hidden items (M-PROC-IMPLIED-ITEMS) { #M-PROC-IMPLIED-ITEMS }
clear errors and correct hygiene and visibility.
Macros should not define magic types on their own, in particular not public ones, or ones that don't rely on namespace tricks.
Some macros want to define types, for example
```rust,ignore
#[my_macro]
struct UserType;
// would expand to
struct UserType;
struct ExtraType;
impl UserType {
fn foo() -> ExtraType { ... };
}
```
This is almost always a bad idea for several reasons:
- they can conflict with existing user-defined types inside the same module,
- if done naively, they can conflict with other expansions of the same macro,
- they can clash with the user's naming conventions,
- they are invisible at source code level and easily forgotten to be re-exported where needed.
While it is possible for users to work around these limitations somewhat, these are paper cuts your users will have to deal with, possibly months after the fact when refactoring otherwise unrelated code.
Note that there is one exception to this rule that has generally acceptable UX, the overloaded use of [namespaces](https://doc.rust-lang.org/reference/names/namespaces.html) made prominent by crates like Rocket:
```rust,ignore
#[my_macro]
fn foo() { ... }
// would expand to
fn foo() { ... }
struct foo;
impl SomeTrait for foo { ... }
```
Here a new type `foo` is introduced with the same name as the function `foo`. Due to Rust's namespace rules they can co-exist and are automatically re-exported with their parent, and due to [Rust's casing rules (C-CASE)](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case) these are highly unlikely to clash with user-defined types. However, they would still not make for a pretty _public_ type, and are therefore mainly used inside root crates to define request handlers or FFI functions.
> ### Namespaces != Modules
>
> Namespaces in Rust have nothing to do with namespaces in other languages. A namespace in C# is approximately a module in Rust. A namespace in Rust
is an esoteric property of names (e.g., `fn foo`, `struct Bar {}`, `moo!`) that decides which 'naming bucket' it lives in inside a module.
---
# Performance Guidelines
## Hot `async` functions reduce stack size (M-ASYNC-STACK-SIZE) { #M-ASYNC-STACK-SIZE }
small async stack sizes and low memcpy overhead.
Functions marked `async` in the hot path should track their future sizes, and take one or more of the following steps to reduce their impact:
- reduction of parameter and rval type size,
- reduction of type size of items held across `.await` points,
- returning `impl Future` and extracting setup logic from `async {}` capture.
> ### Future 'Stack' Sizes
>
> In Futures, what would naively be considered _their stack_, is actually part of a significantly more complicated machinery under their hood.
>
> Regular locals, that only live momentarily between two `.await` points, still remain part of the runtime thread's regular stack. However, any locals that live across `.await` points, or parameters passed during construction, become part of that Future's state machine type, and the layout of this type is currently not as optimized as it could be.
>
> This not only can cause stack-to-heap memcpy operations when creating or boxing Futures, it can also force large upfront stack sizes of the hypothetical most deeply nested cross-async call stack of the involved async function (which, on a side note, is why they can't simply recurse).
>
> ```rust,ignore
> async fn foo(_large: Large) {
> let within_future = [0_u8; 1024]; // Crosses .await below, embedded in `foo` type
> let on_stack = [0_u8; 1024]; // Does not cross .await points, lives on stack
> let sneaky = Droppable::with_size(1024); // Secretly crosses .await point!
> dbg!(&on_stack, &sneaky);
> bar(&within_future).await;
> dbg!(&within_future);
> // <- `sneaky` dropped here, despite otherwise not being used!
> }
>
> let future = foo(Large::new()); // `Large` becomes embedded in `foo` type,
> // blowing up its size, despite it not even
> // being used.
>
> // Here, despite `foo` not running yet, we might consume up to `Large` +
> // 2kb of this thread's stack memory. Once we spawn this is memcpy'ed
> // to runtime Task structure:
> rt.spawn(future);
>```
For many async functions this isn't an issue, as their associated `Future`-cost is negligible. However, functions used along the hot path, that are either called or instantiated frequently (e.g., 1000's of calls per second or concurrent tasks) might benefit from monitoring and optimizations.
Hot futures should be tracked via `size_of_val`:
```rust,ignore
async fn hot() { ... }
#[test]
fn has_reasonable_size() {
let f = hot();
assert!(size_of_val(&f) < ...); // Determine value / limit at first run.
}
```
Then consider a combination of the following:
```rust,ignore
// 1) Return an `impl Future` instead, this prevents large arguments
// from infecting the future size, among others.
fn hot(args: Args) -> impl Future