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.
See C-CASE.
as_
, to_
, into_
conventionsSee C-CONV.
With a few exceptions, the get_
prefix is not used for getters in Rust code.
See C-GETTER.
iter
, iter_mut
, into_iter
See C-ITER.
See C-ITER-TY.
See C-WORD-ORDER.
Copy
, Clone
, Eq
, PartialEq
, Ord
, PartialOrd
, Hash
, Debug
, Display
, Default
should all be implemented when appropriate.
See C-COMMON-TRAITS.
From
, AsRef
, AsMut
See C-CONV-TRAITS.
FromIterator
and Extend
See C-COLLECT.
Serialize
, Deserialize
See C-SERDE.
Send
and Sync
where possibleSee C-SEND-SYNC.
See C-GOOD-ERR.
Hex
, Octal
, Binary
formattingSee C-NUM-FMT.
R: Read
and W: Write
by valueSee C-RW-VALUE.
See C-EVOCATIVE.
See C-MACRO-ATTR.
See C-ANYWHERE.
See C-MACRO-VIS.
See C-MACRO-TY.
See C-CRATE-DOC.
See 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.
?
, not try!
, not unwrap
See C-QUESTION-MARK.
See C-FAILURE.
See C-LINK.
See C-HIDDEN.
unsafe
block has an accompanying justificationNote: This guideline pertains only to safety documentation when performing unsafe operations. See C-FAILURE for guidelines on documenting function safety requirements.
Safety justifications should begin with // SAFETY:
and explain why the unsafe block is sound.
Do
// SAFETY: <why this unsafe operation's safety requirements are met>
Don't
// 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
// 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
// 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
// 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
// SAFETY: The caller has guaranteed that `ptr` points to a valid `T`. unsafe { let x = ptr.read(); }
Don't
// 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
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
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 should be documented according to the same guidelines as unsafe functions.
Unsafe trait definitions should document safety considerations (See C-FAILURE), and unsafe trait implementations should be justified (See “Every unsafe
block has an accompanying justification”))
Do
/// 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
/// 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
blockNote: This guideline depends on a change to linting behavior and cannot yet be followed (tracking issue). 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
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
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; } }
See C-SMART-PTR.
See C-CONV-SPECIFIC
See C-METHOD.
See C-NO-OUT.
See C-OVERLOAD.
Deref
and DerefMut
See C-DEREF.
See C-CTOR.
See C-INTERMEDIATE.
See C-CALLER-CONTROL.
See C-GENERIC.
See C-OBJECT.
See C-NEWTYPE.
bool
or Option
See C-CUSTOM-TYPE.
bitflags
, not enumsSee C-BITFLAG.
See C-BUILDER.
See C-VALIDATE.
See C-DTOR-FAIL.
See C-DTOR-BLOCK.
Debug
See C-DEBUG.
Debug
representation is never emptySee C-DEBUG-NONEMPTY.
See C-SEALED.
See C-STRUCT-PRIVATE.
See C-NEWTYPE-HIDE.
See C-STRUCT-BOUNDS.
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.
Pending topics are tracked in the Rust>Rubric Monorail component.
This rubric contains most of the Rust API Guidelines, however the following official guidelines are omitted:
Cargo.toml
files.crates.io
.crates.io
.The following Fuchsia-specific guidelines are included: