Instruction file imported from rootcause-rs/rootcause (
.github/instructions/rust.instructions.md). Copyright stays with the author.
Rust Coding Conventions and Best Practices
Follow idiomatic Rust practices and community standards when writing Rust code.
These instructions are based on The Rust Book, Rust API Guidelines, RFC 430 naming conventions, and the broader Rust community at users.rust-lang.org.
Rootcause-Specific Constraints
This library is designed to be #![no_std] compatible:
- Prefer
core::overstd::for fundamental types and traits - Use
alloc::for heap-allocated types likeVec,String,Box, etc. - Only use
std::in feature-gated code - some files behind feature flags are allowed to usestd - Be mindful of allocations - the library should work in embedded and constrained environments
- Use
triomphe::Arcinstead ofstd::sync::Arcfor reference counting (this is already established in the codebase)
General Instructions
- Always prioritize readability, safety, and maintainability.
- Use strong typing and leverage Rust's ownership system for memory safety.
- Break down complex functions into smaller, more manageable functions.
- For algorithm-related code, include explanations of the approach used.
- Write code with good maintainability practices, including comments on why certain design decisions were made.
- Handle errors gracefully using
Result<T, E>and provide meaningful error messages. - For external dependencies, mention their usage and purpose in documentation.
- Use consistent naming conventions following RFC 430.
- Write idiomatic, safe, and efficient Rust code that follows the borrow checker's rules.
- Ensure code compiles without warnings.
Patterns to Follow
- Use modules (
mod) and public interfaces (pub) to encapsulate logic. - Handle errors properly using
?,match, orif let. - For rootcause: use
report!()macro andReport<T>types for error handling. - For other code: use
serdefor serialization andthiserrororanyhowfor custom errors. - Implement traits to abstract services or external dependencies.
- Structure async code using
async/awaitandtokioorasync-std(whenstdis available). - Prefer enums over flags and states for type safety.
- Use builders for complex object creation.
- Split binary and library code (
main.rsvslib.rs) for testability and reuse. - Use
rayonfor data parallelism and CPU-bound tasks (whenstdis available). - Use iterators instead of index-based loops as they're often faster and safer.
- Use
&strinstead ofStringfor function parameters when you don't need ownership. - Prefer borrowing and zero-copy operations to avoid unnecessary allocations.
- Use
core::andalloc::imports instead ofstd::when possible for no_std compatibility.
Ownership, Borrowing, and Lifetimes
- Prefer borrowing (
&T) over cloning unless ownership transfer is necessary. - Use
&mut Twhen you need to modify borrowed data. - Explicitly annotate lifetimes when the compiler cannot infer them.
- Use
alloc::rc::Rc<T>for single-threaded reference counting andtriomphe::Arc<T>for thread-safe reference counting (rootcause usestriomphefor no_std compatibility). - Use
core::cell::RefCell<T>for interior mutability in single-threaded contexts andMutex<T>orRwLock<T>for multi-threaded contexts (whenstdis available).
Patterns to Avoid
- Don't use
unwrap()orexpect()unless absolutely necessary—prefer proper error handling. - Avoid panics in library code—return
Resultinstead. - Don't rely on global mutable state—use dependency injection or thread-safe containers.
- Avoid deeply nested logic—refactor with functions or combinators.
- Don't ignore warnings—treat them as errors during CI.
- Avoid
unsafeunless required and fully documented. - Don't overuse
clone(), use borrowing instead of cloning unless ownership transfer is needed. - Avoid premature
collect(), keep iterators lazy until you actually need the collection. - Avoid unnecessary allocations—prefer borrowing and zero-copy operations.
Code Style and Formatting
- Follow the Rust Style Guide and use
rustfmtfor automatic formatting. - Keep lines under 100 characters when possible.
- Place function and struct documentation immediately before the item using
///. - Use
cargo clippyto catch common mistakes and enforce best practices.
Error Handling (Rootcause-Specific)
This library provides structured error reporting, so follow these rootcause-specific patterns:
- Use
Report<T>as the primary error type for this library - Prefer
report!()macro over directReport::new()calls in most cases - Use
IntoReporttrait for converting external errors into reports - Use
ResultExttrait methods like.into_report()for ergonomic error handling - Leverage the library's attachment system to add context to errors
- See
rootcause.instructions.mdfor documentation patterns specific to error reporting
General Error Handling (Non-Rootcause Code)
- Use
Result<T, E>for recoverable errors andpanic!only for unrecoverable errors. - Prefer
?operator overunwrap()orexpect()for error propagation. - Create custom error types using
thiserroror implementstd::error::Error. - Use
Option<T>for values that may or may not exist. - Provide meaningful error messages and context.
- Error types should be meaningful and well-behaved (implement standard traits).
- Validate function arguments and return appropriate errors for invalid input.
API Design Guidelines
Common Traits Implementation
Eagerly implement common traits where appropriate:
Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash,Debug,Display,Default- Use standard conversion traits:
From,AsRef,AsMut - Collections should implement
FromIteratorandExtend - Note:
SendandSyncare auto-implemented by the compiler when safe; avoid manual implementation unless usingunsafecode
Type Safety and Predictability
- Use newtypes to provide static distinctions
- Arguments should convey meaning through types; prefer specific types over generic
boolparameters - Use
Option<T>appropriately for truly optional values - Functions with a clear receiver should be methods
- Only smart pointers should implement
DerefandDerefMut
Future Proofing
- Use sealed traits to protect against downstream implementations
- Structs should have private fields
- Functions should validate their arguments
- All public types must implement
Debug
Testing and Documentation
- Write comprehensive unit tests using
#[cfg(test)]modules and#[test]annotations. - Use test modules alongside the code they test (
mod tests { ... }). - Write integration tests in
tests/directory with descriptive filenames. - Write clear and concise comments for each function, struct, enum, and complex logic.
- Ensure functions have descriptive names and include comprehensive documentation.
- Document all public APIs with rustdoc (
///comments) following the API Guidelines. - Use
#[doc(hidden)]to hide implementation details from public documentation. - Document error conditions, panic scenarios, and safety considerations.
- Examples should use
?operator, notunwrap()or deprecatedtry!macro.
Safety Documentation
All unsafe functions and unsafe blocks MUST follow these documentation patterns to ensure safety requirements are clear and easy to verify:
Safety Documentation in Docstrings
ALL safety documentation MUST use numbered lists, even for single requirements:
Single requirement:
/// # Safety
///
/// The caller must ensure:
///
/// 1. `ptr` is valid and properly aligned
Multiple requirements:
/// # Safety
///
/// The caller must ensure:
///
/// 1. The pointer comes from [`triomphe::Arc::into_raw`]
/// 2. The context type `C` matches the stored type in [`ReportData`]
/// 3. The pointer is not used after calling this function
Safety Comments in Implementation
Every unsafe block MUST have a // SAFETY: comment explaining why the safety requirements of the unsafe operation being performed are upheld.
CRITICAL: The // SAFETY: comment addresses the requirements of the unsafe function/operation being called, NOT the requirements of the outer function you're implementing.
ALL safety comments MUST use numbered lists, even for single requirements:
Single requirement:
// SAFETY: The safety requirement for `ptr.read()` is upheld:
// 1. The pointer is valid and properly aligned because we just created it from a valid reference above
unsafe { ptr.read() }
Multiple requirements:
// SAFETY: The safety requirements for `from_raw` are upheld:
// 1. The pointer comes from `Arc::into_raw` as guaranteed by the caller
// 2. The context type matches because we don't perform any type transmutation
// 3. The pointer is not reused after this call due to the move semantics
unsafe { Report::from_raw(raw) }
Rules for LLMs:
- Identify what unsafe operation is being performed (e.g., calling
from_raw, dereferencing a pointer, calling a vtable function) - Look up that operation's safety requirements (from its
/// # Safetydocs if it's a function, or from Rust docs for intrinsics) - Always use numbered lists: Whether 1 requirement or multiple, always use numbered list format (1. 2. 3.)
- Explain WHY each requirement is satisfied, don't just repeat what the requirement says
- Include helpful context when it aids understanding (e.g., "We know that
self.droppoints todrop::<A>below") - Never skip the comment - every
unsafeblock needs one
Common mistakes to avoid:
- ❌ Don't use bullet points (
-or•) - use numbered lists (1. 2. 3.) for multiple requirements - ❌ Don't repeat the requirement verbatim (e.g., "We must ensure X" → just explain why X is true)
- ❌ Don't be overly verbose - be concise while remaining clear
- ❌ Don't address the outer function's requirements - address the called function's requirements
Project Organization
- Use semantic versioning in
Cargo.toml. - Include comprehensive metadata:
description,license,repository,keywords,categories. - Use feature flags for optional functionality.
- Organize code into modules using
mod.rsor named files. - Keep
main.rsorlib.rsminimal - move logic to modules.
Quality Checklist
Before publishing or reviewing Rust code, ensure:
Core Requirements
- Naming: Follows RFC 430 naming conventions
- Traits: Implements
Debug,Clone,PartialEqwhere appropriate - Error Handling: Uses
Result<T, E>and provides meaningful error types - Documentation: All public items have rustdoc comments with examples
- Testing: Comprehensive test coverage including edge cases
Safety and Quality
- Safety: No unnecessary
unsafecode, proper error handling - Performance: Efficient use of iterators, minimal allocations
- API Design: Functions are predictable, flexible, and type-safe
- Future Proofing: Private fields in structs, sealed traits where appropriate
- Tooling: Code passes
cargo fmt,cargo clippy, andcargo test