blob: 1447e63ba61c7bcb4d877b9ce8d493a271c39068 [file] [log] [blame] [view]
# 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