Rust Coding Style
SkillDev toolsHASH Rust coding style. Use when writing or reviewing Rust code, choosing types, imports, function arguments, or naming.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Rust Coding Style skill
What this skill tells your AI
The instructions your AI receives, as published by hashintel/hash in .agents/skills/rust-coding-style/SKILL.md and read by ahel’s review.
Project-Specific Patterns
- Use the 2024 edition of Rust
- Prefer
derive_moreover manual trait implementations - Feature flags in this codebase use the
#[cfg(feature = "...")]pattern - Invoke
cargo clippywith--all-features,--all-targets, and--no-depsfrom the root - Use
cargo doc --no-deps --all-featuresfor checking documentation - Use
rustfmtto format the code - Use
#[expect(lint, reason = "...")]over#[allow(lint)]
Type System
- Create strong types with newtype patterns for domain entities
- Consider visibility carefully (avoid unnecessary
pub)
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, derive_more::Display)]
pub struct UserId(Uuid);
Async Patterns
- Use
impl Future<Output = T> + Sendin trait definitions:
fn get_data(
&self,
id: String,
) -> impl Future<Output = Result<Data, Report<DataError>>> + Send {
async move {
// Implementation
}
}
Function Arguments
- Functions should never take more than 7 arguments. If a function requires more than 7 arguments, encapsulate related parameters in a struct.
- Functions that use data immutably should take a reference to the data, while functions that modify data should take a mutable reference. Never take ownership of data unless the function explicitly consumes it.
- Make functions
constwhenever possible. - Prefer the following argument types when applicable, but only if this does not reduce performance:
impl AsRef<str>instead of&stror&Stringimpl AsRef<Path>instead of&Pathor&PathBufimpl IntoIterator<Item = &T>when only iterating over the data&[T]instead of&Vec<T>&mut [T]instead of&mut Vec<T>when the function doesn't need to resize the vectorimpl Into<Cow<T>>instead ofCow<T>impl Into<Arc<T>>instead ofArc<T>impl Into<Rc<T>>instead ofRc<T>impl Into<Box<T>>instead ofBox<T>
- Never use
impl Into<Option<_>>as from reading the caller site, it's not visible thatNonecould potentially be passed
From and Into
- Generally prefer
Fromimplementations overIntoimplementations. The Rust compiler will automatically deriveIntofromFrom, but not vice versa. - When converting between types, prefer using the
frommethod overintofor clarity. Thefrommethod makes the target type explicit in the code, whileintorequires type inference. - For wrapper types like
Cow,Arc,Rc,Report, andBox, prefer using explicit constructors (e.g.,Cow::from,Arc::new) instead of.into(). This improves readability by clearly indicating the target type.
Smart Pointers
- When cloning smart pointers such as
ArcandRc, always useArc::clone(&pointer)andRc::clone(&pointer)instead ofpointer.clone(). This explicitly indicates you're cloning the reference, not the underlying data.
Instrumentation
- Annotate functions that perform significant work with
#[tracing::instrument] - Use
tracingmacros (e.g.,trace!,debug!,info!,warn!,error!) instead ofprintln!oreprintln!for logging
Allocations
- Minimize allocations when possible. For example, reuse a
Vecin a loop instead of creating a new one in each iteration. - Prefer borrowed data over owned data where appropriate.
- Balance performance and readability—if an allocation makes code significantly more readable or maintainable, the trade-off may be worthwhile.
Types
- Use newtypes when a value should carry specific semantics beyond its underlying type. This improves type safety and code clarity.
For example:
struct UserId(u64); // instead of `type UserId = u64;` or `u64`
Naming Conventions
When suggesting names for variables, functions, or types:
- Do not prefix test-function names with
test_, this would otherwise result intest::test_<name>names. - Provide a concise list of naming options with brief explanations of why each fits the context
- Choose names of appropriate length—avoid names that are too long or too short
- Avoid abbreviations unless they are widely recognized in the domain (e.g.,
HttporJsonis acceptable, butCtxinstead ofContextis not) - Do not suffix names with their types (e.g., use
usersinstead ofusersList) - Do not repeat the type name in variable names (e.g., use
userinstead ofuserUser)
Crate Preferences
- Use
similar_assertsfor test assertions - Use
instafor snapshot tests - Use
test_logfor better test output (#[test_log::test]) - Use
tracingmacros, notlogmacros - Prefer
tracing::instrumentfor function instrumentation
Import Style
- Don't use local imports within functions, or blocks
- Avoid wildcard imports like
use super::*;, oruse crate::module::*; - Never use a prelude
use crate::prelude::* - Prefer explicit imports to make dependencies clear and improve code readability
- We prefer
coreoverallocoverstdfor imports to minimize dependencies- Use
corefor functionality that doesn't require allocation - Use
allocwhen you need allocation but not OS-specific features - Only use
stdwhen necessary for OS interactions or when usingcore/allocwould be unnecessarily complex
- Use
- Prefer qualified imports (
use foo::Bar; let x = Bar::new()) over fully qualified paths (let x = foo::Bar::new()) for frequently used types - Use
pub usere-exports in module roots to create a clean public API - Avoid importing items with the same name from different modules; use qualified imports
- Import traits using
use module::Trait as _;when you only need the trait's methods and not the trait name itself- This pattern brings trait methods into scope without name conflicts
- Use this especially for extension traits or when implementing foreign traits on local types
// Good - Importing a trait just for its methods:
use std::io::Read as _;
// Example with trait methods:
fn read_file(file: &mut File) -> Result<String, std::io::Error> {
// Read methods available without importing the Read trait name
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// Bad - Directly importing trait when only methods are needed:
use std::io::Read;
// Good - Importing trait for implementing it:
use std::io::Write;
impl Write for MyWriter { /* implementation */ }
// Bad - Wildcard import:
mod tests {
use super::*; // Wildcard import
#[test]
fn test_something() {
// Test implementation
}
}
// Good - Explicit imports:
mod tests {
use crate::MyStruct;
use crate::my_function;
#[test]
fn test_something() {
// Test implementation
}
}
// Bad - Local import:
fn process_data() {
use std::collections::HashMap; // Local import
let map = HashMap::new();
// Implementation
}
// Good - Module-level import:
use std::collections::HashMap;
fn process_data() {
let map = HashMap::new();
// Implementation
}
// Bad - Using std when core would suffice:
use std::fmt::Display;
// Good - Using core for non-allocating functionality:
use core::fmt::Display;
// Bad - Using std when alloc would suffice:
use std::collections::BTreeSet;
// Good - Using alloc for allocation without full std dependency:
use alloc::vec::Vec;
// Appropriate - Using std when needed:
use std::fs::File; // OS-specific functionality requires std
Libraries and Components
- Abstract integrations with third-party systems behind traits to maintain clean separation of concerns
Comments and Assertions
- Do not add comments after a line of code; place comments on separate lines above the code they describe
- When using assertions, include descriptive messages using the optional description parameter rather than adding a comment
- All
expect()messages should follow the format "should ..." to clearly indicate the expected behavior
For example:
// Bad:
assert_eq!(result, expected); // This should match the expected value
// Good:
assert_eq!(result, expected, "Values should match expected output");
// Bad:
some_value.expect("The value is not None"); // This should never happen
// Good:
some_value.expect("should contain a valid value");
Signals
- GitHub stars
- 2k
- Forks
- 122
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
rust-coding-style- Source
- github.com/hashintel/hash