| # Rust Style Guidelines & Best Practices | |
| Please adhere to the following conventions and best practices when writing or | |
| modifying Rust code in this repository: | |
| ## Code Structure & Formatting | |
| * **Code Ordering**: Be consistent about the layout within a file. | |
| * The structure should generally be: | |
| 1. `mod` statements | |
| 2. `use` statements | |
| 3. Core code (structs, enums, impls, functions) | |
| 4. Test module at the bottom: `#[cfg(test)]` | |
| * Structs should always come immediately before their impl and impl traits. | |
| * Full types should be defined before reference types (e.g. if we were | |
| writing the standard library, String before str) | |
| * **Inlining**: If you only use a variable once, prefer inlining it rather | |
| than assigning it to a separate binding. | |
| * **Collect**: Prefer using `.collect()` over manually iterating and | |
| adding/pushing to data structures when possible. | |
| * **Unused Variables**: Variables starting with `_` (e.g. `_var`) should only | |
| be used if they are truly never used. If you begin using a variable that was | |
| prefixed with an underscore, rename it to remove the underscore prefix. | |
| * **Unsafe Code**: All `unsafe` blocks must be prefixed with a | |
| `// Safety: <reason>` comment explaining why the usage is safe. | |
| * **Warnings**: The code (including the tests) should compile with no warnings | |
| and no clippy lint errors. | |
| * If you ever believe that #[allow] is required to bypass the linter: | |
| * You *must* get permission from the user. | |
| * You *must* write a comment above the allow saying why we need it. | |
| * You *must* put it in as specific a place as is feasible. Avoid, for | |
| example, allowing a lint for a whole file. | |
| ## Imports & Use Statements | |
| * **Test-only Imports**: Imports that are only used inside tests should be | |
| moved into a module block guarded by `#[cfg(test)]`. | |
| ## Documentation | |
| * Anything marked as pub should have a docstring (starts with `///`). | |
| ## Coding guidelines | |
| * Data members should not be public without good reason | |
| * **FFI Boundary**: No FFI function calls (such as `extern "C"` blocks or raw | |
| FFI calls) are allowed outside of the `ffi` submodule. The only exception is | |
| `intern_string`. | |
| * **No Statics**: Do not use global static variables or `thread_local!` | |
| structures. They hinder concurrency, unit testing isolation, and | |
| multi-session safety. Always prefer passing state and context explicitly via | |
| arguments or trait objects. | |
| * **Dynamic dispatch**: Never use dynamic dispatch (`dyn Trait`) in the hot | |
| path (any code that could be called an arbitrary number of times by a | |
| starlark rule). | |
| * Avoid using dynamic dispatch (`dyn Trait`) in general if you can, but | |
| you may use it to increase testability of code if it is not in the hot | |
| path. | |
| * Eg. Using `dyn Trait` to support a "real" version in production code and | |
| a fake one in test code. | |
| * `collect_repr` and `collect_str` | |
| * collect_repr should be defined on starlark types iff `Debug` is | |
| available on a type. It should just `write!(collector, "{:?}").unwrap()` | |
| * collect_str should be defined on starlark types iff `Display` is | |
| available on a type. It should just `write!(collector, "{}").unwrap()` | |
| * **Error Handling**: Prefer using custom error enums with `thiserror` for | |
| crate-level errors, rather than generic strings or custom hand-written | |
| Display implementations. Let exceptions and failures bubble up naturally. | |
| * **Crate Encapsulation**: Keep modules, structs, and fields private or | |
| `pub(crate)` by default to maintain clean boundaries. Only use `pub` for APIs | |
| that are intended to be consumed by other crates. | |
| # C++ Style guidelines and best practices | |
| * Never use `#pragma once` - use header guards instead. |