blob: 350d431eb8537866410973c4783434269c8e8b4f [file] [view]
# 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.