- Rust Style Guide
- Background
- Follow community Rust manner rules
- Apply API scheme rules in context
- Code organization
- Prefer small crates [chrome]
- Use flat document layout for rust_library sources [chrome]
- Name origin records and leaf modules following the chief user-defined item
- Don’t execute APIs in non-leaf modules
- Don’t use glob imports
- A use declaration should import items from one module
- Prefer locally-scoped trait imports
- Qualify calls to top-level functions alongside a component name
- Type names have to be imported and used unqualified
- Use the suitable import scheme [chrome]
- Code formatting and aesthetics
- Ownership and borrowing
- Function inputs and outputs
- Types
- Traits
- Add trait implementations whenever they rotate into necessary
- Implement known norm archive traits alternatively of tradition methods
- Use #[derive()] if possible
- Generics versus dyn Trait
- When to define a trait versus an inherent implementation
- Extension traits
- Specific direction for crucial traits
- Pair crucial From impls alongside definitive constructor methods
- Options structs should execute Default
- Concurrency
- Macros
- Naming
- When and how to compose documentation
- Other Rust features
- Unsafe code
- No unstable features
- Getters and setters
- Implement expected API sets together
- Use const functions responsibly
- Avoid overloading for non-mathematical purposes
- Apply #[non_exhaustive] to publicly-exposed enums
- Apply #[non_exhaustive] to community structs alongside community fields
- #[must_use] on functions
- #[must_use] on types
- Do not run code before main()
- Implementation concerns
- Don’t overuse category inference
- Prefer form matching complete check-then-unwrap
- Prefer anticipate complete unwrap
- Don’t omit patterns on non_exhaustive enums whenever matching
- Use environment strings to explain assertions
- Don’t use iterator combinator chains whenever straightforward repeat is clearer
- Don’t call panic::catch_unwind
- Prefer format! / write! for chain and substitution
- Use the correct per-codebase test framework
This document is a style guide for the use of the Rust programming language at Google. In this context, “style” refers to additional than fair formatting; it includes direction on API and implementation design.
Rust is used in many places, including google3, Android, ChromeOS, Fuchsia, and Chrome, and we’d akin for Rust use at Google to be accordant the two inside and throughout codebases. In supplement to bringing consistency to our Rust code, manner direction additionally helps established a norm of quality.
Background
Scope
The current range of authority of the rules outlined in this guide:
- In Android, Chromium, and another codebases: This manner guide has been developed in consultation alongside another codebases, and they average to start applying it. These codebases may have their own rules concerning how strictly this is expected to be applied. Expect additional data current as these codebases clasp this guide.
Goals of the manner guide
By and large, we have this document for the identical reasons we have a C++ manner guide.
Most manner rules arrive from one or additional of the goals listed below.
Style rules should prioritize readability and maintainability complete writeabilityWe anticipate our code to be about for lengthy periods of time, and we consider a small bit of additional attempt applied during code penning to be worth it in the lengthy run if the manner regulation improves readability.
Style rules should favor improved automationLarge-Scale Changes (LSCs) are average at Google, and consistency helps make LSCs easier to transport out.
Style rules have to be accordant alongside the wider Rust ecosystem anywhere possibleThe broader Rust ecosystem already has a beautiful fine amount of consistency in style, and we should depend on that anywhere imaginable to decrease astonishment and make it small activity to study Google’s Rust style.
Style rules should drag their weightEvery Rust developer and reviewer at Google has to keep these manner rules in mind: as such, we should observe out to contain rules that have a tangible benefit.
Style rules may assistance evade insignificant manner disagreementsThere are many cases anywhere sensible group may differ concerning a style, and we anticipate that disagreement to display up frequently on CLs. Having a manner regulation helps avoid having to rehash this all time. Rules in this category that do not have other benefits have to be kept to a minimum.
Follow community Rust manner rules
Follow the authoritative community Rust manner rules to the degree they don’t conflict with this manner guide.
-
- Most code formatting conventions are enforced automatically by rustfmt.
A “conflict” would be a case anywhere this manner guide suggests a form that is discouraged, or oppose to the chief encouraged form in the Rust style guide.
Why does the direction in this manner guide deviate from regular Rust manner rules?
Most manner guides have three goals: consistency, readability, and encouraging good API design. Regardless of the particular choices made, it helps everyone to have these choices made in advance to bounds bikeshedding.
This manner guide shares those goals, but it additionally has an additional goal not seen as often: smoothing the way for Large-Scale Changes (LSCs). Large codebases like many of the ones at Google occasionally need to run codebase-spanning refactorings that are assisted by tooling: for this to activity well, the code should be item tooling has an uncomplicated period operating with. Consistency helps for this, but so does having deliberate refactor-friendly patterns.
Apply API scheme rules in context
When applying rules that connect to API design, you should differentiate two types of code:
-
Library code
- Intended for use by another projects (applications or another libraries).
- Solves a comparatively broad problem.
-
Application code
- Intended for use in a sole application.
- Solves a additional particular problem.
For library code, manner guide rules that involvement API scheme are binding.
For application code, the rules on API scheme are a powerful recommendation. As you use them, keep in intellect that since use code is written to resolve a specific problem, it should mostly favor simplicity and directness complete broad reusability. Avoid premature generalization and abstraction; concentration on what is required to encounter the contiguous need.
Code organization
Terminology:
-
A crate is a component of compilation. A crate continually contains a nameless Rust module, the “root module”.
-
A Rust module is a component of code institution inside a crate. Rust modules are used to authority visibility of names. Modules can be nested. They can also be used to divided code throughout records and directories.
The following sections communicate that in Chromium, it is preferable to not make use of all possibilities of splitting modules throughout files.
Prefer small crates [chrome]
Prefer small crates in Chromium; ideally, but not necessarily, a sole file (focusing on one abstraction). If your code can be divided up into small crates, do so.
A crate corresponds to a sole rust_static_library (chrome) rule, which may contain one or additional .rs files.
Additional considerations:
-
The base component is allowed to re-export community APIs from leaf submodules, making submodules an implementation item of the crate (as users don’t need to cognize concerning these submodules).
-
This regulation concerning small crates applies likewise to crates alongside and without submodules. First, measure whether the APIs need to be in the identical crate, and lone afterward regard whether submodules could assistance arrange the APIs.
-
If you decide to use submodules, prosecute the expected document structure.
Larger crates are allowed when trait impl coherence rules force multiple types and traits to be included in one library, or if you’re building a ample foundational library.
Rationale
Fine-grained archive targets are recommended in Chromium because of the enormous size of our codebase, non-local effects of adding requirements in the build graph, and to enhance achievement of parallel compilation and build caching.
Use flat document layout for rust_library sources [chrome]
In Chromium .rs origin records belonging to one rust_library (one Rust crate) must live in a sole directory.
Don’t use subdirectories to arrange records that pertain to a sole crate. If organizing one crate’s records into subdirectories feels necessary, this is a strong sign that the crate has grown too large and should be split.
This crate layout is intentionally distinct from the accepted Cargo directory construction typically used in open-source repositories.
Exception: Rust code in //third_party that is additionally built with Cargo should prosecute the accepted Cargo directory structure.
Rationale
The method in Chromium is driven by the following factors:
- Based on the penchant for small crates, most rust_library targets volition lone merge one origin file.
- Because of the complete point, creating a directory for all rust_library target would outcome in most directories containing lone one origin file.
- Multi-file rust_library targets should not create dedicated directories for submodules either; this ensures a accordant approach.
Name origin records and leaf modules following the chief user-defined item
When a leaf component has a chief user-defined item (e.g., Foo), the module should be named following that item (e.g., mod foo and foo.rs).
Leaf modules that are not chiefly concerning any particular idea can be named as desired.
Rationale
This makes it uncomplicated to acknowledge the content/purpose of a origin document or leaf module at a glance.
Don’t execute APIs in non-leaf modules
The lone items in non-leaf modules have to be component declarations (not definitions) and re-exports.
Note that the definition of leaf modules allows the following sub-modules: mod test and cross-language binding modules akin #[cxx::bridge].
Exceptions:
- Having a small amount of center “ties things together” implementation code in a non-leaf component may be fine. The goal is to not have a lot of code in these modules.
Rationale
This makes it easier to discover things, and avoids mixing construction and contents.
Don’t use glob imports
Don’t use glob imports, apart from in the following situations anywhere you may optionally do so at your own discretion:
- use super::* for inline modules
- use enum::* in method bodies of enums, and functions that are primarily dealing alongside that enum
- pub use submodule::* for non-leaf modules
-
C++ interop: Since the identical namespace (in C++) is a submodule in different Rust crates (in Rust), you may merge the namespaces into a personal copy using glob imports. For example:
mod absl { pub use status::absl::*; pub use strings::absl::*; }
In this hypothetical example, you can use absl::Status and absl::StrCat from Rust, the identical way you would from C++, alternatively of orthography them as status::absl::Status and strings::absl::StrCat.
This must lone be used for merging C++ namespaces, and the resulting merged module must not be public.
If using third-party crates that advance a prelude, it is up to the reviewer’s discretion to authorize it.
This additionally method that defining preludes have to be avoided.
Rationale
Glob imports addition toil for code janitors operating LSCs, and create it harder to know anywhere a category comes from in contexts without xrefs.
A use declaration should import items from one module
This regulation corresponds to the rustfmt environment imports_granularity = Module.
Basically, there should not be any ::’s inner the {..} obstacle of a use statement. For example:
use foo::Foo; use foo::bar::Bar; use foo::bar::Baz;
use foo::Foo; use foo::bar::{Bar, Baz};
use foo::{Foo, bar::Bar};
Prefer locally-scoped trait imports
Import traits inside function bodies, unless the trait’s methods are used extensively throughout the file.
fn foo(x: u8) { // Import the trait inside a function, not at the component scope. use std::str::FromStr; let string: String = x.parse(); }
Qualify calls to top-level functions alongside a component name
To use top-level functions defined in another modules or crates, import their module and call them alongside modulename::functionname(). For example:
use std::mem; let x = mem::transmute(y);
use std::mem::transmute; let x = transmute(y);
Exceptions:
- When importing functions alongside sufficiently disambiguated names, e.g., functions from C++ (where it is representative to call top-level (free) functions directly).
-
In Chromium, whenever importing functions from small, single-purpose first-party crates.
- When the component name itself is prone to collisions. For example, whenever the module name is a C++ namespace name, and multiple crates define the same submodules.
Rationale
Many Rust codebases consistently keep a manner specified that unqualified lowercase names are assumed local, so foo() can be assumed to be from the identical module. This regulation mostly exists to evade reviewer disagreements, the advantage is mostly consistency and a insignificant border on study code without xrefs.
Type names have to be imported and used unqualified
By default, types and traits have to be referenced in code without using their module path, e.g., code should use Regex, not regex::Regex, alongside a use regex::Regex at the top of the document or possibly in the applicable function.
Exceptions:
- In cases anywhere there are ambiguities, it is satisfactory to immediately reference types alongside average names akin io::Error. It is additionally satisfactory to rename (use std::io::Error as IoError).
- In cases anywhere you have a ample “group” of types, e.g. the hir component in rustc which contains a ample figure of HIR types.
- In macros and generated code; anywhere requiring a use is counterproductive.
- In code alongside many of dependencies, anywhere importing everything would guide to a extremely ample import obstacle alongside small benefit.
Rationale
This regulation mostly exists to evade reviewer disagreements.
Use the suitable import scheme [chrome]
- Chrome Use chromium::import!().
Code formatting and aesthetics
Use the suitable formatting tool
Use the suitable formatting tool (e.g., git cl format in Chromium).
Configuration (for manner guide authors)
A codebase may choose to not use the following rustfmt options if they care about construction on stable and the choice is unavailable on their stable Rust:
-
Maximum of 100 columns per line: max_width = 100
-
Usage of lowercase alphabets in hex literals (0xab123cdef as opposed to 0xAB123CDEF): hex_literal_case = Lower
-
Macro matcher formatting: format_macro_matchers = true
-
Usage of escapes for invisible characters in literals. E.g., if you desire to have content containing a Zero-Width joiner, use \u{200D}.
Opting out of automatic formatting
You may opt out of automatic formatting using #[rustfmt::skip], but lone in the following situations:
- Manual alignment – If you have a situation-specific logic to have a snippet of code row up in a particular way (e.g., multiple akin lines of code that you desire to remain aligned for readability, or a matrix-constructing function).
- Generated code (ideally, use a genrule instead).
Important: Formatting should not be skipped for individual manner choices.
Ownership and borrowing
Avoid Rc and Arc
Prefer distinctive ownership and borrowing complete Rc and Arc for managing memory.
Exception:
- Use Rc and Arc whenever the substitute ownership and borrowing structure would be unreasonably complex to write, understand, or maintain. This is a judgment call to be discussed during code review.
Rationale
Generally, Rust allows representing entity graphs without using shared ownership smart pointers. These pointers add runtime overhead and their own unique complexity. Engineers coming from another languages power lean to use Rc and Arc too quickly.
Prefer borrows complete profound copies
Use .clone() whenever you genuinely need a profound copy of an object.
Avoid using .clone() as a substitute for borrowing. Design code that works naturally alongside Rust’s ownership and borrowing rules. However, if the borrowing relationships rotate into excessively complex, .clone() can be a pragmatic choice.
Consider the achievement needs of the code during choosing between cloning and borrowing.
Note: This direction does not use to cloning Rc or Arc pointers. Cloning reference-counted pointers does not create a profound copy, and is an expected way to use them.
Rationale
Deep copies can be costly to create because of recollection allocation and data copying.
Function inputs and outputs
Accept elastic references for immutable data
When functions obtain borrowed, immutable data, use a citation category that affords the most flexibility. Pass references to the data itself alternatively of references to astute pointers, containers, or wrappers.
| Use | Instead of | Why? |
|---|---|---|
| &T | &Box<T> | Borrow the data inner the box |
| &[T] | &Vec<T> | Accept any piece alternatively of particular containers |
| &str | &String | Accept a cord piece alternatively of references to owned strings |
| Option<&T> | &Option<T> | Borrow the data inner the Option |
Exceptions:
- When expecting non-linear character data specified as Rope/Cord.
For context, see Clippy lints borrowed_box and ptr_arg.
Rationale
Accepting citation types that don’t citation particular astute pointers, containers, or wrappers makes functions additional versatile and performant: callers can continue data held in a assortment of ways without copying it first.
Iterators vs. owned collections in come back types
Prefer returning iterators (either tangible types or impl Iterator) where possible complete returning Vec<T>.
Note: In use code, returning a Vec<T> is fine if it simplifies the code and if elasticity and achievement advantages of iterators are not clearly needed.
Exceptions:
- Most callers volition need to build an owned vector.
- The function’s implementation naturally constructs a vector.
- The returned value volition apt need to be randomly indexed.
- The algorithm plant improved as a for iteration and the forced allocation doesn’t matter too much.
- The function’s intended intent is creating a new owned collection.
- The function is returning a pre-existing vector.
Rationale
Highly reusable archive code mostly should strive to provision additional flexible and performant APIs. Returning an iterator allows callers to choose how to consume the data: collect it into a vector, procedure it in a loop, or chain further iterator adapters. Some of these options authorize users to avoid unnecessary allocations.
In use code, the use patterns for a function are frequently easier to predict. If it is apparent that achievement is not a concern, or that data would be collected into a vector anyway, returning a vector makes for a simpler API.
Iterators vs. slices in function parameters
Highly reusable archive code should obtain iterators or impl IntoIterator if it lone needs to iterate complete the input.
Note: In use code, accepting a piece &[T] is fine if it simplifies the code and if elasticity and achievement advantages of iterators are not clearly needed.
Rationale
Highly reusable archive code mostly should strive to provision additional flexible and performant APIs. Functions that obtain iterators recommendation callers:
- More elasticity (data can be supplied from any collection)
- Efficiency (data does not need to be copied into a adjacent part of memory)
- Composability (the function plant improved alongside iterator chains)
In use code, the use patterns for a function are frequently easier to predict. If it is apparent that achievement is not a concern, or that callers would always have a vector anyway, accepting a piece makes for a simpler API.
Accepting Into
When designing archive APIs that obtain a T but users are apt to have various types, regard accepting impl Into<T> alternatively of T if call-site ergonomics are paramount.
Tip: This have to be stable against code bloat concerns; it is not a requirement to use many of Into<T> in your code.
Note: Application code should nearly never use impl Into<T> parameters.
Rationale
Using impl Into<T> allows a function to immediately obtain assorted arguments of various compatible types. For example, a function accepting impl Into<String> could be called alongside a String or a &str literal directly.
The tradeoff current is balancing the convenience of avoiding a .into() at the callsite against the disbursal of generics (more complex API signature, increased code size, sometimes equal category conclusion difficulties).
Prefer enum complete bool in APIs
Use enum function parameters alternatively of bool in cases anywhere the intent of the indicator is not obvious.
If you do use bool, regard using an inline comment to document the purpose of the bool in an disagreement list.
Reporting errors
Choose the error reporting scheme that fits your API contract: Option, Result, or panic!.
Use Option for uncomplicated failures anywhere the logic is apparent and the caller would not advantage from additional details. This is finest for standard, anticipated outcomes akin a missing value or a parsing mismatch.
// Good: The logic of the nonaccomplishment is apparent (integer overflow). No further // particulars could be fairly provided. fn checked_add(a: u32, b: u32) -> Option<u32> { ... }
Use Result for recoverable failures anywhere the visitant needs context to handle the error. These are frequently “unavoidable” errors caused by external factors (like I/O or network) that equal correctly implemented code cannot prevent alongside checks onward of time. Nevertheless, the visitant can reasonably anticipate the kinds of errors (a file-related function can neglect alongside an I/O error). The error particulars authorize the visitant to differentiate nonaccomplishment modes and recover appropriately.
// Good: The visitant needs to cognize *why* it unsuccessful (NotFound vs PermissionDenied). fn open_file(path: &Path) -> Result<File, io::Error> { ... }
Use panic! to study bugs and violated invariants. These errors are avoidable: the program is missing a check, and is calling an procedure without fulfilling a precondition. These defects must be fixed in the code, not handled at runtime, although coarse-grained improvement power be imaginable with catch_unwind.
// Good: Panics if the invariant (index must be in bounds) is violated. // This indicates a bug in the caller's logic. impl<T> MyVec<T> { pub fn remove(&mut self, index: usize) -> T { if index >= self.len() { panic!("index out of bounds"); } // ... } }
Note: Some codebases may choose to evade panics entirely, and necessitate even broken invariants to be reported via Result.
Exceptions:
- Developer-facing binaries may use panics for non-fatal errors.
- Tests may use panics for assertions.
Types
Newtypes versus category aliases
Use a newtype instead of a category pseudonym in the following cases:
- If the category you are defining has notable semantic differences.
- For types that justify an API of their own.
- If you desire the elasticity to alter the underlying category later, especially if you don’t authority all usages of your type.
- For category safety, if it’s uncomplicated for the types to get blended up. For example, a Date category may use newtypes to differentiate Day, Month, and Year numbers in its inner calculations.
Prefer named sectors complete tuples and non-newtype tuple structs
Prefer types alongside named sectors complete tuples, as tuple indexes (.0, .1, …) quickly rotate into confusing.
Exceptions:
- Newtype tuple structs (tuple structs alongside lone one field).
- In extremely local contexts anywhere there is improbable to be any confusion.
Traits
Add trait implementations whenever they rotate into necessary
As a broad rule, low-level archive code should anticipate users’ needs proactively, during use code should add trait impls as needed.
Implement known norm archive traits alternatively of tradition methods
If a method matches the intent and behavior of a norm archive trait, implement that trait. For example, alternatively of penning fn from_str(&str) -> Self on Foo, you should impl FromStr for Foo.
It is satisfactory to have the two the method and the trait impl for discoverability reasons.
For context, see Clippy lint should_implement_trait (list of traits).
Use #[derive()] if possible
Manual implementations have to be avoided if they can be expressed as a #[derive()].
Generics versus dyn Trait
In libraries, favor accepting generics (T: Trait or impl Trait) in function parameters complete dyn Trait.
Exceptions:
- Cases anywhere code size matters.
Rationale
-
Efficiency
- Additional indirection: Creating a dyn Trait requires environment up a fat pointer, and a vtable lookup is required for all call of a function on the trait.
- If a function takes a dyn Trait, the compiler can’t execute certain optimizations that it could if the function was generic, specified as inlining calls to functions of the trait.
-
Some traits are not dyn compatible. For example, a trait is not dyn compatible if it has generic functions, associated functions without a oneself parameter, or functions that return Self.
When to define a trait versus an inherent implementation
Define a trait lone for the following purposes:
-
To allow generic programming (to use T: Trait in constraints of generic functions and types).
-
To obtain or come back an impl Trait or dyn Trait.
-
To define an extension trait (but see additionally the caveats in the division on expansion traits).
In all another cases anywhere you desire to execute functionality for a type, use an inherent implementation.
Don’t use traits merely to arrange code.
Extension traits
An extension trait is a trait that adds methods to a category from a distinct crate.
What differentiates an expansion trait from another traits is that its sole purpose is to add methods to the type; it is not intended for use in generics (T: Trait, impl Trait) or energetic dispatch (dyn Trait). To signify this, the name of an expansion trait should transport the suffix ~Ext (for example, ForeignTypeExt).
In general, the squad that owns the first category should additionally own the extension trait.
Exceptions:
- Extension methods that are expected to rotate into fine known and provide significant value, for example, in foundational libraries / frameworks.
Note: This regulation is specifically concerning expansion traits. It is comparatively common to define a trait as an input trait (e.g., DateLike) and execute it on a bunch of abroad types.
Examples
extern crate foreign_crate; use foreign_crate::ForeignType; trait ForeignTypeExt { fn additional_method(&self); } impl ForeignTypeExt for ForeignType { fn additional_method(&self) { ... } }
It is additionally imaginable to define an expansion trait for all category that implements some abroad trait. Effectively, this is equal to adding default methods to the abroad trait:
extern crate foreign_crate; use foreign_crate::ForeignTrait; trait ForeignTraitExt { fn additional_method(&self); } impl<T: ForeignTrait> ForeignTraitExt for T { fn additional_method(&self) { ... } }
Rationale
When the identical squad owns the expansion trait and the category being extended, this helps evade method name conflicts between the basis category and the expansion trait or between two expansion traits.
Specific direction for crucial traits
-
Types alongside community fields: Consider implementing traits implemented by all subfields.
-
Error types: Implement the Copy trait whenever possible. Copy types are simpler to oversee and propagate since they can be passed by value without moving ownership. An example of a case anywhere this is not imaginable is if the error must merge non-Copy sectors akin a String.
-
Copy: Implement whenever it is fairly apparent that the category will continue to be Copy in the future.
-
Clone: Most types should execute Clone unless they portray some external resource.
-
Default: Implement if the category implements new() or alternatively has a default state. For context, see Clippy lint new_without_default.
-
PartialEq / Eq: If you desire users to use the == operator, prefer implementing PartialEq complete Eq. When you do execute Eq, be aware of the additionally required reflexivity (details: https://doc.rust-lang.org/std/cmp/trait.Eq.html).
-
PartialOrd / Ord: If you desire users to use the < or > operators, favor implementing PartialOrd complete Ord. When you do implement Ord, be conscious that it requires total ordering (details: https://doc.rust-lang.org/std/cmp/trait.Ord.html).
-
Map keys
- If you think it is apt that a category volition be used as a map key or otherwise cached, execute Hash and Eq.
- If, in addition, the category has a apparent total ordering, execute Ord (and any traits it needs).
Pair crucial From impls alongside definitive constructor methods
If a category has an crucial From impl that is expected to be among the primary ways of constructing it, add an inherent ::new(), ::new_from_foo()¸ or ::from_foo() method.
Options structs should execute Default
Structs alongside all-public sectors that are chiefly used as a “bag of options” for a function should execute Default.
Rationale
This enables convenient use of struct update syntax (Struct { a: 0, ..Default::default()} ).
Concurrency
Handling poison
Never attain inner a poisoned mutex to get at its insides, no matter how tempting the use case may seem.
Don’t use async / .await [chrome]
Don’t use async / .await in Chromium.
</section>
Don’t commencement new threads through the Rust norm archive [chrome]
In Chromium, favor to use sequences alternatively of threads.
Use fuchsia_sync::Mutex [Fuchsia]
Use Fuchsia’s mutex type, fairly than one from std::sync or parking_lot.
Macros
When considering how to province an API, favor in order:
-
Regular Rust functions, types, traits, objects, and modules. (Avoid macros, code generation, and metaprogramming anywhere possible).
-
macro_rules!-style macros.
-
Other code generation strategies (e.g. genrule)
-
proc_macro macros.
When you decide to use a macro or code generation, delight measure the benefits against the costs:
- IDEs, Code Search and another developer tools volition activity small fine on code that uses macros.
- Macros can create dialects of the tongue that create it harder for engineers to activity throughout the codebase.
- Large measure changes are additional difficult and costly to apply.
Rationale
Prefer non-macro facilities: Macros can create dialects of the tongue that make it harder for engineers to activity throughout the codebase. When a audience sees a usage of a macro, they can create small assumptions concerning that code than if it didn’t use a macro. Macros are harder to compose correctly than regular Rust code.
Use macro_rules! for uncomplicated code generation: Since Rust macros run on the AST, they can be small susceptible than textual code generation.
Naming
Naming methods
Follow the Rust norm archive manner for naming methods. See additionally the guidance on naming conventions in the Comprehensive Rust course.
Specific considerations:
- Use prefixes specified as as_...(), to_...(), or into_...() alongside the appropriate oneself convention. See also this section of the Rust API Guidelines and the Clippy lint wrong_self_convention.
Naming lifetimes
Lifetimes should have descriptive names, particularly category lifetimes.
Exceptions:
- Type lifetimes that are extremely generic abstractions akin Cow.
- It is fine to use anonymous lifetimes ('_) anywhere they work.
- Single-character lifetimes are fine whenever they are merely expressing the relationship between a method’s input and output, or needed for a local higher-ranked life limits (for<'a>). Up to two specified local lifetimes are fine, if additional are needed, we powerfully advise you to use descriptive names.
Naming generic parameters
Use a abbreviated name for your generic parameters (either sole or multi character), related to the benevolent of item they represent. Single-letter generics are okay, but the letter chosen have to be slightly indicative of the entity type, ideally taken from the most crucial trait bound.
T or U are fine whenever the generics are in a comparatively unbound context; i.e. it is an API intended for use alongside most types, akin Vec<T>.
Naming expansion traits
When naming an extension trait, provision the name an ~Ext suffix (for example, ForeignTypeExt).
Naming initialization functions
Follow these guidelines whenever naming initialization functions:
-
Constructors: Use new(), or new_with_foo() if there is additional than one. If the constructor returns a Result, use try_new().
-
Value-to-value conversions: Use from_foo() alongside an connected From impl. See here for particulars on whenever an procedure can be considered to be a value-to-value conversion.
Avoid abbreviations
Don’t use abbreviations (especially acronyms and initialisms) that someone outside your project is improbable to know. Don’t abbreviate by deleting letters within a word.
See additionally the Google C++ Style Guide.
Exceptions:
- Common abbreviations established in Wikipedia.
- Abbreviations that are improbable to be confusing to group exterior of your project, for example IpAddr. You and your reviewer should create this determination. In general, the penchant should motionless be not to abbreviate.
When and how to compose documentation
Add records comments to community APIs, possibly using the missing_doc lint to assistance alongside this.
Best practices for records comments:
-
Consider including examples: It is not necessary for all function to have examples, but adequate functionality have to be covered in examples somewhere to provision an idea of how the crate works.
-
Always document panic cases for functions which may panic because of malformed input.
-
Clearly document invariants for unsafe functions and traits.
-
Use capitalization and punctuation.
-
Don’t explain the basics of the programming language. For example, don’t do the following:
struct MockFileSystem { ... } /// A imitate implementation of the FileSystem trait, intended to be used in component tests. impl FileSystem for MockFileSystem
Exceptions:
-
Comments should say item that the code does not already say. If there’s nothing insightful to say, it is improved to omit the comment than to repeat the function name in a trivial way. For example, don’t do the following (it’s fine to authorize the lint in that case):
/// Removes an element. pub fn remove_element() -> Element {}
-
It is mostly fine to omit records on idiosyncratic enum variants, especially if their name is self-explanatory.
How to format records comments
Follow these guidelines whenever penning records comments:
- Use /// comments to document items following the comment, specified as functions, structs, enums, traits, and category aliases.
- Use //! comments to document the component or the archive itself. These comments have to be placed at the top of the file.
- Follow the rules laid out in the authoritative Rust manner guide on doc comments.
- Use Markdown syntax.
- Use average comment syntax (//) for non-documentation comments, specified as implementation comments, TODOs that are not engaging for users, commented out origin code, etc.
- Prefer to document things in a straightforward manner, using /// and //! comments, fairly than generating records from macros. This makes the documentation immediately readable whenever viewing or modifying the origin code.
It is acceptable, and equal encouraged, to mix doc comments (/// and //!) and ordinary comments (//). If you have one item you desire to document publicly, and another item you desire to note as an implementation comment, afterward use both! For example:
/// Finds the archetypal `FooItem` alongside the stated `name`, or `None` if not present. // TODO: crbug.com/12345 - `name` have to be &str, not String. pub fn get_named(items: &[FooItem], name: String) -> Option<&FooItem> {...}
Rationale
Many, although not all, languages have a distinct commenting gathering (such as extra * characters) for doc comments as opposed to implementation comments. Rust and Python are notable for not fair having distinct commenting conventions, but an genuine distinct syntax entirely. This has tangible benefits to the Rust ecosystem:
- Doc comments can be consumed by tooling, including records generators like rustdoc, IDE features, and Kythe.
- Doc comments have a norm construction and formatting rules (Markdown), allowing for affluent content in those contexts.
- Errors in applying doc comment syntax (e.g., applying it to an expression instead of a documentable item) are linted against, catching documentation errors early. If there are no lint errors, the doc comments are guaranteed to be apparent to records tooling, removing a flat of flakiness and unpredictability from the records process.
- Doc comments are a part of the AST, and so can be produced and consumed by macros, including the two macro_rules! macros and procedural macros.
Other Rust features
Unsafe code
Don’t compose unsafe code unless there is no way to execute the same functionality alongside harmless code.
If you do have to compose unsafe code:
- Encapsulate unsafe code in a harmless idea and isolate it from business logic as much as possible. Ideally, unsafe code have to be in a separate crate alongside few requirements beyond the norm library.
- Add a safety comment starting alongside SAFETY: that explains why the code is correct.
- Add a specialized justification for unsafe code. Depending on context, this can be a justification for an idiosyncratic unsafe obstacle or a additional general justification that covers an complete document or build target. The justification should answer these questions:
- Why is this code not written in an equal harmless way?
- If the logic is performance, are there benchmarks?
No unstable features
Don’t use unstable features.
Exceptions:
- Android Embedded programming may necessitate the use of certain unstable features. While assessment volition be required to enable them (to assistance hunt for alternatives), features entirely required to build an relic correctly volition mostly be approved.
- Chrome Style arbiters may blanket-approve a small catalog of features for the complete codebase. Typically, lone features that have a apparent way to stabilization volition be approved.
- Fuchsia See guidance here.
Getters and setters
Writing and naming getters and setters should prosecute these guidelines:
- foo() for a getter that returns a T by value.
- foo_mut() for a getter that returns a &mut T.
- take_foo() for a getter that “clears” the value (by environment it to None or several another default state) and returns the old value.
- set_foo() for a setter that takes a T by value.
- with_foo() / with_mut_foo() for an accessor that operates via a closure (see particulars below). Such an accessor is invoked as object.with_foo(|foo| frob(foo)).
- replace_foo() for a setter that returns the old value.
Examples
impl Stuff { fn foo(&self) -> &Foo { ... } fn foo_mut(&mut self) -> &mut Foo { ... } /// Sets "baz" asset to `None` and returns the old value. fn take_baz(&mut self) -> Option<Baz> { ... } fn set_foo(&mut self, foo: Foo) { ... } /// Calls `f` alongside a get of the "bar" property. /// Returns the come back value of `f`. fn with_bar<R>(&self, f: impl FnOnce(&Bar) -> R) -> R { ... } /// Calls `f` alongside a mutable get of the "bar" property. /// Returns the come back value of `f`. fn with_mut_bar<R>(&self, f: impl FnOnce(&mut i32) -> R) -> R { ... } /// Sets "foo" asset to `foo` and returns the old value. fn replace_foo(&mut self, foo: Foo) -> Foo { ... } }
More on accessors that obtain closures
Accessors that obtain a closure (.with_foo()) are typically invoked as object.with_foo(|foo| do_something_with(foo)). They can be a fine choice in the following situations:
-
When the asset is contained in an inner mutability wrap (e.g. Mutex or RefCell). These mechanisms need to constrain admission to the property to a scope, and the closure provides this scope. This is usually preferable to immediately returning the RAII entity that implements the scope (e.g. MutexGuard<T>) since the second would leak an implementation detail.
-
When follow-up activity or validation is needed following a asset is changed and a set_foo() accessor cannot be used (for example since the property’s type is costly to copy or cannot be cloned at all).
A with_foo() accessor volition frequently authorize the closure to come back a value and forward it by returning it from with_foo().
If you need to provision two closure-accepting accessors, one for read-only access and one for mutable access, it is average to name them with_foo() and with_mut_foo(). Otherwise, with_foo() is frequently additionally used for mutating accessors.
Implement expected API sets together
If you execute len(), additionally execute is_empty(). For context, see Clippy lint len_without_is_empty.
Do not admission items marked #[doc(hidden)] that you don’t own
Treat items marked #[doc(hidden)] as private. Don’t admission them unless you own the code.
Use const functions responsibly
Mark a community function as const lone if you anticipate it to remain const in the future (or can commit to assisting clients migrate whenever it changes).
Rationale
It’s a breaking alter to rotate a const function into a regular function.
Avoid overloading for non-mathematical purposes
Use controller overloads for their first mathematical/logical purpose, not just to create the code additional concise (as “syntax sugar”).
Apply #[non_exhaustive] to publicly-exposed enums
If an enum is part of a community API used by another teams, grade it #[non_exhaustive].
Exceptions:
- The enum volition never change. For example:
- Enums describing an existing ontology (e.g., enum Color { Red, Green, Blue }).
- Enums alongside two states that could be Booleans (e.g., enum Visibility { Visible, Hidden })
- The enum may alter in the future, but you are committed to fixing resulting breakages via an LSC.
For context, see Clippy lint exhaustive_enums.
Rationale
Marking an enum #[non_exhaustive] forces users to contain a wildcard arm (_ => ...) in equivalent statements. This ensures code continues to compile whenever new variants are added to the enum later.
This regulation is chiefly crucial for enums used throughout squad boundaries, because the range of the breakage could be significant. Enums used lone a small number of times can’t create a ample breakage and thus may disregard this rule.
Apply #[non_exhaustive] to community structs alongside community fields
Structs alongside community sectors that are part of a community API used by another teams should be marked #[non_exhaustive].
Exceptions:
- The struct sectors are improbable to change, e.g., since they depict an existing ontology (e.g., a Rectangle type) or the reviewer can alternatively be confident that they volition not change.
- The struct sectors may alter in the future, but you are committed to fixing resulting breakages via an LSC.
- You desire to allow your users to use functional document update (FRU) struct initialization syntax (Person { name: "foo", ..Default::default() }).
Rationale
Adding a new site (even a personal one) to a struct alongside all community sectors is a breaking change, since callers can no longer name all sectors in a struct literal.
A #[non_exhaustive] struct can’t be constructed alongside the struct literal syntax, apart from for the code in the identical crate. This allows new personal sectors to be added without breaking users.
This regulation is chiefly crucial for structs used throughout squad boundaries, because the range of the breakage could be significant. Structs used lone a small figure of times can’t create a ample breakage and thus may disregard this rule.
#[must_use] on functions
Use #[must_use] on functions in these cases:
-
The visitant cannot use the function correctly if they disregard the return value. This includes clean functions (functions without flank effects), such as HashMap::get().
-
Any function whose come back value represents an error or alternatively critical information concerning the function’s behavior. If the come back category is already annotated #[must_use] (which is true of Result, for example), the function does not need to be annotated #[must_use].
#[must_use] on types
Use #[must_use] on types intended to be returned by functions that would otherwise be annotated #[must_use] according to the rules above.
Examples:
-
The category is an error category (similar to Result or StatusOr).
-
The category represents a lazily evaluated computation. In another words, the function call doesn’t do item by itself, and the come back value needs to be accessed to logic several desired flank effect.
Do not run code before main()
Do not logic code to run before main(), for example by using techniques such as the ctor crate.
Implementation concerns
Don’t overuse category inference
Add adequate definitive types so that person acquainted alongside the codebase can figure out what the inferred types are.
Prefer to add the types to changeable declarations, fairly than as turbofishes. If the compiler forces you to add a turbofish, it is mostly fine to add a _ inference marker.
Specific situations in which you should likely add types:
- When calling methods alongside a generic come back type, specified as .into() or .parse().
- For the outcome of complex iterator chains.
Prefer form matching complete check-then-unwrap
Avoid possibly panicking operations whenever the input data guarantees they cannot panic. For example, alternatively of using Option::unwrap on a value known to be valid, use form matching to demonstrate correctness to the compiler.
// Good: The equivalent arm proves to the compiler that `x` is `Some`. // A panic is evidently not possible. if let Some(value) = option { use_value(value); }
// Bad: Correctness of `unwrap` relies on a runtime inspect that `option` is `Some`. if option.is_some() { use_value(option.unwrap()); }
Prefer anticipate complete unwrap
If a panic is necessary since an invariant cannot be expressed in the type system, favor anticipate complete unwrap to document the reason.
Don’t omit patterns on non_exhaustive enums whenever matching
Prefer to catalog all enum variants in equivalent statements complete non_exhaustive enums.
If you don’t attention concerning particular variants, location them in the wildcard arm. If you only attention concerning particular variants, use an if let or a let … alternatively {}.
Examples:
#[non_exhaustive] pub enum Shape { Circle, Square, Triangle, }
fn process_shape(shape: Shape) { match shape { Shape::Circle => println!("It's a circle"), Shape::Square => println!("It's a square"), // Triangle and any forthcoming variants don't necessitate any actions. Shape::Triangle | _ => {} } }
fn process_shape(shape: Shape) { if let Shape::Circle = shape { println!("It's a circle"), } }
fn process_shape(shape: Shape) { match shape { Shape::Circle => println!("It's a circle"), _ => {} } }
Exceptions:
- You lone attention concerning particular variants and equivalent is cleaner than if let.
This is covered by the built-in lint non_exhaustive_omitted_patterns which you can allow on your code.
Use environment strings to explain assertions
While several assertions are self-explanatory, many advantage from additional context that clarifies why an assertion is expected to hold, or what caused it to be violated. Use the optional format cord disagreement for assert! to communicate this context.
You should continually regard adding context, but particularly in the following situations:
- If you’re thinking of adding a comment to the assertion. Use a format string instead: It has the advantage that it volition appear in logs.
- If you anticipate failing assertions to be seen by users. In this case, a human-readable clarification is additional helpful than a Rust expression.
- If logging a value helps in understanding why the assertion failed.
- To explain whether a failing assertion method that
- The client misused an API (i.e., they should alter their use of the API), or
- The implementation contains a bug (i.e., the client should document a bug report).
Examples
Explaining why an assertion should hold
assert!( values.is_sorted(), "`values` is populated from a `PriorityQueue`, so we anticipate it to be sorted" );
Logging a value to explain why the assertion failed
assert!(refund > 0, "Refund must be positive, got {refund:?});
Flagging misuse of an API
impl Database { ... fn execute_query(&self, query: &str) { assert!(self.stream.is_some(), "Call connect() before executing queries"); } ... }
Don’t use the environment cord merely to reiterate the condition
assert(!values.is_sorted(), "values should continually be sorted");
Don’t use iterator combinator chains whenever straightforward repeat is clearer
Avoid using combinator chains in ways that hurt readability. Specifically:
- Avoid lengthy and complex combinator chains.
- Don’t build combinator chains in the header of a for loop. Instead, assign the combinator sequence to a variable, afterward consume that changeable in the for loop.
- Avoid Iterator::for_each unless you’re convinced you have a fine reason. A for iteration is normally easier to read.
Combinators are preferable for shorter chains and for cases anywhere the combinator itself provides a lot of logic that would alternatively have to be written out in the corresponding for loop.
Examples
Complex combinator chains
Here’s an example of a combinator sequence that is difficult to peruse since it involves conditionals and mutates external state:
let mut error_count = 0; let _processed_items: Vec<_> = items .into_iter() .map(transformation_that_may_fail) .map(|result| { if result.is_err() { error_count += 1; } result }) .filter_map(Result::ok) .collect();
The corresponding for iteration is easier to read:
let mut error_count = 0; let mut processed_items = Vec::with_capacity(items.len()); for item in items { match transformation_that_may_fail(item) { Ok(val) => processed_items.push(val), Err(_) => error_count += 1, } }
Combinator chains in for loops
Avoid constructing a combinator sequence in the header of a for loop:
for bug in bug_descriptions .iter() .filter_map(|description| parse_bug_description(description).ok()) .filter(|bug| bug.priority <= Priority::P1) { assign_bug(bug); }
Instead, allocate the combinator sequence to a variable. This reduces clutter, and the changeable name documents what the combinator sequence does:
let urgent_bugs = bug_descriptions .iter() .filter_map(|description| parse_bug_description(description).ok()) .filter(|bug| bug.priority <= Priority::P1); for bug in urgent_bugs { assign_bug(bug); }
When to favor a combinator chain
Here’s an example anywhere a combinator sequence is preferable:
let threshold_exceeded = values.iter().any(|&v| v > threshold);
The corresponding for iteration is harder to peruse and reinvents the logic contained in any():
let mut threshold_exceeded = false; for v in values { if v > threshold { threshold_exceeded = true; break; } }
Don’t call panic::catch_unwind
Use of panic::catch_unwind is prohibited in first-party Rust code.
Exceptions:
- It is satisfactory as a defence in degree scheme as lengthy as it is never relied upon for any properties. A codebase that is already attempting to avoid panics may choose to cover crucial admission points alongside a catch_unwind recovery scheme fair in case a panic slips through. Code authors must convince the reviewer that the codebase is doing most of what it can to avoid panics. This elimination lone applies to code that has the possibility of being run alongside panic=unwind (so this does not use to Chrome, Android, Fuchsia).
Rationale
Rust code may be used in many environments, and panic::catch_unwind volition only function correctly in environments alongside panic=unwind. While the final panic disposition of prod binaries has not yet been determined, Chrome, Android, and Fuchsia all necessitate panic=abort.
Prefer format! / write! for chain and substitution
Prefer format!() and write!() to another alternatives in the following situations:
- To concatenate additional than two strings. For example, use format!("{a}{b}{c}") alternatively of a + b + c.
- To execute substitutions on a literal cord (instead of using str::replace).
Rationale
- format! / write! necessitate small allocations than concatenating strings using +.
- Prefer norm format strings to inventing your own templating syntax.
Use the correct per-codebase test framework
Most codebases use their own particular test example fairly than #[test].
-
Chrome Use rust_gtest_interop. This makes it imaginable to merge Rust tests into C++ test binaries.
-
Fuchsia Use #[fuchsia::test]. See here for additional details.