| # 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 |