| # Rust Rubric | |
| [TOC] | |
| This document lists conventions to follow when writing Rust in the Fuchsia Source Tree. These conventions are a combination of best practices, project preferences, and some choices made for the sake of consistency. | |
| <!-- TODO add collapsible <details> sections around guideline bodies --> | |
| <!-- TODO inline text of upstream guidelines once fuchsia-specific guidelines settle --> | |
| ## Guidelines | |
| ### Naming | |
| #### Casing conforms to Rust idioms | |
| See [C-CASE](https://rust-lang.github.io/api-guidelines/naming.html#c-case). | |
| #### Ad-hoc conversions follow `as_`, `to_`, `into_` conventions | |
| See [C-CONV](https://rust-lang.github.io/api-guidelines/naming.html#c-conv). | |
| #### Getter names follow Rust convention | |
| With a few exceptions, the `get_` prefix is not used for getters in Rust code. | |
| See [C-GETTER](https://rust-lang.github.io/api-guidelines/naming.html#c-getter). | |
| #### Methods on collections that produce iterators follow `iter`, `iter_mut`, `into_iter` | |
| See [C-ITER](https://rust-lang.github.io/api-guidelines/naming.html#c-iter). | |
| #### Iterator type names match the methods that produce them | |
| See [C-ITER-TY](https://rust-lang.github.io/api-guidelines/naming.html#c-iter-ty). | |
| #### Names use a consistent word order | |
| See [C-WORD-ORDER](https://rust-lang.github.io/api-guidelines/naming.html#c-word-order). | |
| ### Interoperability | |
| #### Types eagerly implement common traits | |
| `Copy`, `Clone`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`, `Hash`, `Debug`, `Display`, `Default` should all be implemented when appropriate. | |
| See [C-COMMON-TRAITS](https://rust-lang.github.io/api-guidelines/interoperability.html#c-common-traits). | |
| #### Conversions use the standard traits `From`, `AsRef`, `AsMut` | |
| See [C-CONV-TRAITS](https://rust-lang.github.io/api-guidelines/interoperability.html#c-conv-traits). | |
| #### Collections implement `FromIterator` and `Extend` | |
| See [C-COLLECT](https://rust-lang.github.io/api-guidelines/interoperability.html#c-collect). | |
| #### Data structures implement Serde's `Serialize`, `Deserialize` | |
| See [C-SERDE](https://rust-lang.github.io/api-guidelines/interoperability.html#c-serde). | |
| #### Types are `Send` and `Sync` where possible | |
| See [C-SEND-SYNC](https://rust-lang.github.io/api-guidelines/interoperability.html#c-send-sync). | |
| #### Error types are meaningful and well-behaved | |
| See [C-GOOD-ERR](https://rust-lang.github.io/api-guidelines/interoperability.html#c-good-err). | |
| #### Binary number types provide `Hex`, `Octal`, `Binary` formatting | |
| See [C-NUM-FMT](https://rust-lang.github.io/api-guidelines/interoperability.html#c-num-fmt). | |
| #### Generic reader/writer functions take `R: Read` and `W: Write` by value | |
| See [C-RW-VALUE](https://rust-lang.github.io/api-guidelines/interoperability.html#c-rw-value). | |
| ### Macros | |
| #### Input syntax is evocative of the output | |
| See [C-EVOCATIVE](https://rust-lang.github.io/api-guidelines/macros.html#c-evocative). | |
| #### Macros compose well with attributes | |
| See [C-MACRO-ATTR](https://rust-lang.github.io/api-guidelines/macros.html#c-macro-attr). | |
| #### Item macros work anywhere that items are allowed | |
| See [C-ANYWHERE](https://rust-lang.github.io/api-guidelines/macros.html#c-anywhere). | |
| #### Item macros support visibility specifiers | |
| See [C-MACRO-VIS](https://rust-lang.github.io/api-guidelines/macros.html#c-macro-vis). | |
| #### Type fragments are flexible | |
| See [C-MACRO-TY](https://rust-lang.github.io/api-guidelines/macros.html#c-macro-ty). | |
| ### Documentation | |
| #### Crate level docs are thorough and include examples | |
| See [C-CRATE-DOC](https://rust-lang.github.io/api-guidelines/documentation.html#c-crate-doc). | |
| #### All items have a rustdoc example | |
| See [C-EXAMPLE](https://rust-lang.github.io/api-guidelines/documentation.html#c-example). | |
| > Note: this guideline is not reasonable to enforce for targets which build on Fuchsia until | |
| > doctests are supported on Fuchsia targets. See | |
| > [#38215](https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=38215). | |
| #### Examples use `?`, not `try!`, not `unwrap` | |
| See [C-QUESTION-MARK](https://rust-lang.github.io/api-guidelines/documentation.html#c-question-mark). | |
| <!-- TODO how does this interact with avoiding ? in tests? --> | |
| #### Function docs include error, panic, and safety considerations | |
| See [C-FAILURE](https://rust-lang.github.io/api-guidelines/documentation.html#c-failure). | |
| #### Prose contains hyperlinks to relevant things | |
| See [C-LINK](https://rust-lang.github.io/api-guidelines/documentation.html#c-link). | |
| #### Rustdoc does not show unhelpful implementation details | |
| See [C-HIDDEN](https://rust-lang.github.io/api-guidelines/documentation.html#c-hidden). | |
| #### Every `unsafe` block has an accompanying justification | |
| Note: This guideline pertains only to safety documentation when performing unsafe operations. See | |
| [C-FAILURE](https://rust-lang.github.io/api-guidelines/documentation.html#c-failure) for guidelines | |
| on documenting function safety requirements. | |
| Safety justifications should begin with `// SAFETY: ` and explain why the unsafe block is sound. | |
| **Do** | |
| ```rust | |
| // SAFETY: <why this unsafe operation's safety requirements are met> | |
| ``` | |
| **Don't** | |
| ```rust | |
| // Safety: <...> | |
| // [SAFETY] <...> | |
| // <...> | |
| // SAFETY: Trust me. | |
| ``` | |
| Unsafe code should explain why the unsafe block is necessary and why the code contained within the | |
| block is sound. If there are safe alternatives which may appear suitable but cannot be used, the | |
| reason why they cannot be used should be documented as well. | |
| **Do** | |
| ```rust | |
| // SAFETY: The `bytes` returned from our string builder are guaranteed to be | |
| // valid UTF-8. We used to call `from_utf8`, but this caused performance issues | |
| // with large inputs. | |
| let s = unsafe { String::from_utf8_unchecked(bytes) }; | |
| ``` | |
| **Don't** | |
| ```rust | |
| // SAFETY: We shouldn't have to validate `bytes`, and the safe version is slow. | |
| let s = unsafe { String::from_utf8_unchecked(bytes) }; | |
| ``` | |
| Justifications should directly address the requirements for the operation. It's okay to summarize as | |
| long as all of the requirements are addressed. | |
| **Do** | |
| ```rust | |
| // SAFETY: The caller has guaranteed that `ptr` is valid for reads, properly | |
| // aligned, and points to a properly-initialized `T`. | |
| unsafe { | |
| let x = ptr.read(); | |
| } | |
| ``` | |
| **Do** | |
| ```rust | |
| // SAFETY: The caller has guaranteed that `ptr` points to a valid `T`. | |
| unsafe { | |
| let x = ptr.read(); | |
| } | |
| ``` | |
| **Don't** | |
| ```rust | |
| // SAFETY: `ptr` is safe to read. | |
| unsafe { | |
| let x = ptr.read(); | |
| } | |
| ``` | |
| Safety justifications should address *why* an operation is justified, not just that an operation is | |
| justified. | |
| **Do** | |
| ```rust | |
| const BUFFER_LEN: usize = 1024; | |
| fn partially_init(n: usize) -> MaybeUninit<[i32; BUFFER_LEN]> { | |
| // These asserts ensure our safety conditions are met later on | |
| const _: () = assert!(BUFFER_LEN <= 1024); | |
| assert!(n < BUFFER_LEN); | |
| let mut buffer = MaybeUninit::<[i32; BUFFER_LEN]>::uninit(); | |
| let ptr = buffer.as_mut_ptr().cast::<i32>(); | |
| for i in 0..n { | |
| // SAFETY: | |
| // - `ptr` points to the first `i32` of `buffer`. | |
| // - `buffer` has space for BUFFER_LEN elements and we asserted that | |
| // `n < BUFFER_LEN`. | |
| // - We asserted that `BUFFER_LEN <= 1024`, so `size_of::<i32>() * i` | |
| // is at most 4096 which is less than `isize::MAX` and `usize::MAX`. | |
| let element = unsafe { &mut *ptr.add(i) }; | |
| *element = i as i32; | |
| } | |
| buffer | |
| } | |
| ``` | |
| **Don't** | |
| ```rust | |
| const BUFFER_LEN: usize = 1024; | |
| fn partially_init(n: usize) -> MaybeUninit<[i32; BUFFER_LEN]> { | |
| // Why are these asserts here? | |
| const _: () = assert!(BUFFER_LEN <= 1024); | |
| assert!(n < BUFFER_LEN); | |
| let mut buffer = MaybeUninit::<[i32; BUFFER_LEN]>::uninit(); | |
| let ptr = buffer.as_mut_ptr().cast::<i32>(); | |
| for i in 0..n { | |
| // SAFETY: | |
| // - `ptr` is in bounds or one byte past the end of an allocated object. | |
| // - `ptr + i` is also in bounds. | |
| // - The computed offset, in bytes, doesn't overflow an `isize`. | |
| // - The offset being in bounds does not rely on "wrapping around" the | |
| // address space. | |
| let element = unsafe { &mut*ptr.add(i) }; | |
| *element = i as i32; | |
| } | |
| buffer | |
| } | |
| ``` | |
| #### Unsafe traits are documented, unsafe trait impls are justified | |
| Unsafe traits should be documented according to the same guidelines as unsafe functions. | |
| Unsafe trait definitions should document safety considerations (See [C-FAILURE][c-failure]), and | |
| unsafe trait implementations should be justified (See | |
| ["Every `unsafe` block has an accompanying justification"][every-unsafe-block])) | |
| [c-failure]: https://rust-lang.github.io/api-guidelines/documentation.html#c-failure | |
| [every-unsafe-block]: #every-unsafe-block-has-an-accompanying-justification | |
| **Do** | |
| ```rust | |
| /// A labeler that always returns unique labels. | |
| /// | |
| /// # Safety | |
| /// | |
| /// Every time `create_unique_label()` is called on the same labeler, it must | |
| /// return a distinct `u32`. | |
| unsafe trait UniqueLabeler { | |
| /// Returns a new unique label. | |
| fn create_unique_label(&mut self) -> u32; | |
| } | |
| struct SequentialLabeler { | |
| next: Option<u32>, | |
| } | |
| // SAFETY: `create_unique_label()` will always return the next sequential label | |
| // or panic if all available labels are exhausted. | |
| unsafe impl UniqueLabeler for SequentialLabeler { | |
| fn create_unique_label(&mut self) -> u32 { | |
| if let Some(next) = self.next { | |
| self.next = (next < u32::MAX).then(|| next + 1); | |
| next | |
| } else { | |
| panic!("sequential unique labels exhausted"); | |
| } | |
| } | |
| } | |
| ``` | |
| **Don't** | |
| ```rust | |
| /// A labeler that always returns unique labels. | |
| unsafe trait UniqueLabeler { | |
| /// Returns a new unique label. | |
| fn create_unique_label(&mut self) -> u32; | |
| } | |
| struct SequentialLabeler { | |
| next: u32, | |
| } | |
| unsafe impl UniqueLabeler for SequentialLabeler { | |
| fn create_unique_label(&mut self) -> u32 { | |
| // This will have correct panicking behavior in debug builds because | |
| // integer overflow is trapped. In a release build, this will still | |
| // overflow but our labeler will not panic! | |
| let result = self.next; | |
| next += 1; | |
| result | |
| } | |
| } | |
| ``` | |
| #### Unsafe operations are always in an `unsafe` block | |
| Note: This guideline depends on a change to linting behavior and cannot yet be followed | |
| ([tracking issue](https://fxbug.dev/42176206)). Continue to adhere to other guidelines in this section. | |
| `unsafe` functions are not considered unsafe contexts in Fuchsia. Unsafe operations must always be | |
| located inside an `unsafe` block, even if they are in an `unsafe` function body. | |
| **Do** | |
| ```rust | |
| unsafe fn clear_slice(ptr: *mut i32, len: usize) { | |
| assert!(len.checked_mul(mem::size_of::<i32>()).unwrap() < isize::MAX); | |
| // SAFETY: | |
| // - The caller has guaranteed that `ptr` points to `len` consecutive, valid | |
| // i32s and that the data at behind `ptr` is not simultaneously accessed | |
| // through any other pointer. | |
| // - We asserted that the total size of the slice is less than isize::MAX. | |
| let slice = unsafe { slice::from_raw_parts_mut(ptr, len) }; | |
| for x in slice.iter_mut() { | |
| *x = 0; | |
| } | |
| } | |
| ``` | |
| **Don't** | |
| ```rust | |
| unsafe fn clear_slice(ptr: *mut i32, len: usize) { | |
| // We forgot to assert that the length of the slice is less than isize::MAX! | |
| // If we justified our call to from_raw_parts_mut, we would have been much | |
| // more likely to remember. | |
| let slice = slice::from_raw_parts_mut(ptr, len); | |
| for x in slice.iter_mut() { | |
| *x = 0; | |
| } | |
| } | |
| ``` | |
| ### Predictability | |
| #### Smart pointers do not add inherent methods | |
| See [C-SMART-PTR](https://rust-lang.github.io/api-guidelines/predictability.html#c-smart-ptr). | |
| #### Conversions live on the most specific type involved | |
| See [C-CONV-SPECIFIC](https://rust-lang.github.io/api-guidelines/predictability.html#c-conv-specific) | |
| #### Functions with a clear receiver are methods | |
| See [C-METHOD](https://rust-lang.github.io/api-guidelines/predictability.html#c-method). | |
| #### Functions do not take out-parameters | |
| See [C-NO-OUT](https://rust-lang.github.io/api-guidelines/predictability.html#c-no-out). | |
| #### Operator overloads are unsurprising | |
| See [C-OVERLOAD](https://rust-lang.github.io/api-guidelines/predictability.html#c-overload). | |
| #### Only smart pointers implement `Deref` and `DerefMut` | |
| See [C-DEREF](https://rust-lang.github.io/api-guidelines/predictability.html#c-deref). | |
| #### Constructors are static, inherent methods | |
| See [C-CTOR](https://rust-lang.github.io/api-guidelines/predictability.html#c-ctor). | |
| ### Flexibility | |
| #### Functions expose intermediate results to avoid duplicate work | |
| See [C-INTERMEDIATE](https://rust-lang.github.io/api-guidelines/flexibility.html#c-intermediate). | |
| #### Caller decides where to copy and place data | |
| See [C-CALLER-CONTROL](https://rust-lang.github.io/api-guidelines/flexibility.html#c-caller-control). | |
| #### Functions minimize assumptions about parameters by using generics | |
| See [C-GENERIC](https://rust-lang.github.io/api-guidelines/flexibility.html#c-generic). | |
| #### Traits are object-safe if they may be useful as a trait object | |
| See [C-OBJECT](https://rust-lang.github.io/api-guidelines/flexibility.html#c-object). | |
| ### Type safety | |
| #### Newtypes provide static distinctions | |
| See [C-NEWTYPE](https://rust-lang.github.io/api-guidelines/type-safety.html#c-newtype). | |
| #### Arguments convey meaning through types, not `bool` or `Option` | |
| See [C-CUSTOM-TYPE](https://rust-lang.github.io/api-guidelines/type-safety.html#c-custom-type). | |
| #### Types for a set of flags are `bitflags`, not enums | |
| See [C-BITFLAG](https://rust-lang.github.io/api-guidelines/type-safety.html#c-bitflag). | |
| #### Builders enable construction of complex values | |
| See [C-BUILDER](https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder). | |
| ### Dependability | |
| #### Functions validate their arguments | |
| See [C-VALIDATE](https://rust-lang.github.io/api-guidelines/dependability.html#c-validate). | |
| #### Destructors never fail | |
| See [C-DTOR-FAIL](https://rust-lang.github.io/api-guidelines/dependability.html#c-dtor-fail). | |
| #### Destructors that may block have alternatives | |
| See [C-DTOR-BLOCK](https://rust-lang.github.io/api-guidelines/dependability.html#c-dtor-block). | |
| ### Debuggability | |
| #### All public types implement `Debug` | |
| See [C-DEBUG](https://rust-lang.github.io/api-guidelines/debuggability.html#c-debug). | |
| #### `Debug` representation is never empty | |
| See [C-DEBUG-NONEMPTY](https://rust-lang.github.io/api-guidelines/debuggability.html#c-debug-nonempty). | |
| ### Future Proofing | |
| #### Sealed traits protect against downstream implementations | |
| See [C-SEALED](https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed). | |
| #### Structs have private fields | |
| See [C-STRUCT-PRIVATE](https://rust-lang.github.io/api-guidelines/future-proofing.html#c-struct-private). | |
| #### Newtypes encapsulate implementation details | |
| See [C-NEWTYPE-HIDE](https://rust-lang.github.io/api-guidelines/future-proofing.html#c-newtype-hide). | |
| #### Data structures do not duplicate derived trait bounds | |
| See [C-STRUCT-BOUNDS](https://rust-lang.github.io/api-guidelines/future-proofing.html#c-struct-bounds). | |
| ## Updating the guidelines | |
| To propose additions or modifications, open a CL and cc | |
| [`fuchsia-rust-api-rubric@google.com`] to ensure it is reviewed. Use any | |
| feedback to iterate on the proposal. | |
| Once feedback has been addressed, any one of the [OWNERS] of this file who is | |
| not the proposal author may act as facilitator and move the proposal to last | |
| call. The facilitator will send an email to [`fuchsia-rust-api-rubric@google.com`] | |
| announcing last call on the proposal. The proposal will be open for feedback | |
| for 7 calendar days. | |
| At the end of the last call period and once relevant concerns have been | |
| discussed or addressed, a facilitator will comment on the CL with a final | |
| decision based on the review feedback and discussion. Any controversial | |
| decisions should be made with adequate public discussion of the relevant issues, | |
| and should include the rationale in the CL comment. The decision outcome should | |
| also be sent to the email thread. | |
| If a proposal is accepted, the facilitator will leave a +2 and the author can | |
| then submit it. | |
| [`fuchsia-rust-api-rubric@google.com`]: mailto:fuchsia-rust-api-rubric@google.com | |
| [OWNERS]: https://cs.opensource.google/fuchsia/fuchsia/+/master:docs/development/api/OWNERS | |
| ### Pending Topics | |
| Pending topics are tracked in the [Rust][buganizer] Issue Tracker component. | |
| [buganizer]: https://issues.fuchsia.dev/issues?q=componentid:1368214%20status:open | |
| ## Relationship with upstream Rust API guidelines | |
| This rubric contains most of the [Rust API Guidelines][rust-guidelines], however the following | |
| official guidelines are omitted: | |
| * [C-FEATURE](https://rust-lang.github.io/api-guidelines/naming.html#c-feature) as Fuchsia does not | |
| currently support features for crates. | |
| * [C-METADATA](https://rust-lang.github.io/api-guidelines/documentation.html#c-metadata) as Fuchsia | |
| does not maintain internal `Cargo.toml` files. | |
| * [C-HTML-ROOT](https://rust-lang.github.io/api-guidelines/documentation.html#c-html-root) as | |
| Fuchsia does not currently publish most Rust code to `crates.io`. | |
| * [C-RELNOTES](https://rust-lang.github.io/api-guidelines/documentation.html#c-relnotes) as most | |
| Rust code in Fuchsia "lives at HEAD". | |
| * [C-STABLE](https://rust-lang.github.io/api-guidelines/necessities.html#c-stable) as Fuchsia does | |
| not currently publish most Rust code to `crates.io`. | |
| * [C-PERMISSIVE](https://rust-lang.github.io/api-guidelines/necessities.html#c-permissive) as all of | |
| Fuchsia's Rust code is under the Fuchsia license. | |
| The following Fuchsia-specific guidelines are included: | |
| * [Every `unsafe` block has an accompanying justification][safety-justification-guideline] | |
| * [Unsafe traits are documented, unsafe trait impls are justified][unsafe-traits-guideline] | |
| * [Unsafe operations are always in an `unsafe` block][unsafe-ops-in-unsafe-blocks-guideline] | |
| [safety-justification-guideline]: #every-unsafe-block-has-an-accompanying-justification | |
| [unsafe-traits-guideline]: #unsafe-traits-are-documented-unsafe-trait-impls-are-justified | |
| [unsafe-ops-in-unsafe-blocks-guideline]: #unsafe-operations-are-always-in-an-unsafe-block | |
| [rust-guidelines]: https://rust-lang.github.io/api-guidelines/about.html |