rust-coding
SKILL.md
Naming Conventions (Rust-Specific)
| Rule | Correct | Incorrect |
|---|---|---|
No get_ prefix for methods |
fn name(&self) |
fn get_name(&self) |
| Iterator methods | iter() / iter_mut() / into_iter() |
get_iter() |
| Conversion naming | as_ (cheap), to_ (expensive), into_ (ownership) |
Mixed usage |
static variables uppercase |
static CONFIG: Config |
static config: Config |
const variables |
const BUFFER_SIZE: usize = 1024 |
No restriction |
General Naming
// Variables and functions: snake_case
let max_connections = 100;
fn process_data() { ... }
// Types and traits: CamelCase
struct UserSession;
trait Cacheable {}
// Constants: SCREAMING_SNAKE_CASE
const MAX_CONNECTIONS: usize = 100;
static CONFIG: once_cell::sync::Lazy<Config> = ...
Solution Patterns
Pattern 1: Conversion Methods
impl Buffer {
// as_ - cheap, view conversion
pub fn as_slice(&self) -> &[u8] {
&self.data
}
// to_ - expensive, allocating conversion
pub fn to_vec(&self) -> Vec<u8> {
self.data.clone()
}
// into_ - consuming, ownership transfer
pub fn into_vec(self) -> Vec<u8> {
self.data
}
}
Pattern 2: Newtype Pattern
// ✅ Domain semantics with newtypes
struct Email(String);
struct UserId(u64);
struct Meters(f64);
impl Email {
pub fn new(s: impl Into<String>) -> Result<Self, EmailError> {
let email = s.into();
if email.contains('@') {
Ok(Self(email))
} else {
Err(EmailError::Invalid)
}
}
}
Pattern 3: Error Handling
// ✅ Good: propagate errors
fn read_config() -> Result<Config, ConfigError> {
let content = std::fs::read_to_string("config.toml")
.map_err(ConfigError::from)?;
toml::from_str(&content)
.map_err(ConfigError::Parse)
}
// ❌ Avoid: panic in library code
fn read_config() -> Config {
std::fs::read_to_string("config.toml").unwrap() // panic!
}
// ✅ Use expect when invariant guaranteed
fn get_user(&self) -> &User {
self.user.as_ref()
.expect("user always initialized in constructor")
}
Pattern 4: String Handling
// ✅ Accept &str in APIs
fn greet(name: &str) {
println!("Hello, {}", name);
}
// ✅ Use Cow when might need owned
use std::borrow::Cow;
fn process(input: &str) -> Cow<str> {
if input.contains("special") {
Cow::Owned(input.replace("special", "normal"))
} else {
Cow::Borrowed(input)
}
}
// ✅ Pre-allocate when size known
let mut s = String::with_capacity(100);
Data Type Guidelines
| Rule | Description | Example |
|---|---|---|
| Use newtype | Domain semantics | struct Email(String) |
| Use slice patterns | Pattern matching | if let [first, .., last] = slice |
| Pre-allocate | Avoid reallocations | Vec::with_capacity() |
| Avoid Vec abuse | Fixed size → array | let arr: [u8; 256] |
String Guidelines
| Rule | Description |
|---|---|
ASCII data use bytes() |
s.bytes() faster than s.chars() |
Might modify → Cow<str> |
Borrow or owned |
Use format! for concat |
Better than + operator |
Avoid nested contains() |
O(n*m) complexity |
Error Handling Guidelines
| Rule | Description |
|---|---|
Use ? to propagate |
Don't use try!() macro |
expect() over unwrap() |
When value guaranteed |
Use assert! for invariants |
At function entry |
Memory and Lifetimes
| Rule | Description |
|---|---|
| Meaningful lifetime names | 'src, 'ctx not just 'a |
RefCell use try_borrow |
Avoid panics |
| Use shadowing for conversions | let x = x.parse()? |
Concurrency Guidelines
| Rule | Description |
|---|---|
| Define lock ordering | Prevent deadlocks |
| Atomics for primitives | Not Mutex<bool> |
| Choose memory ordering carefully | Relaxed/Acquire/Release/SeqCst |
Async Guidelines
| Rule | Description |
|---|---|
| CPU-bound → sync | Async for I/O |
| Don't hold locks across await | Use scoped guards |
Macro Guidelines
| Rule | Description |
|---|---|
| Avoid macros (unless necessary) | Prefer functions/generics |
| Macro input like Rust | Readability first |
Deprecated Patterns → Modern
| Deprecated | Modern | Version |
|---|---|---|
lazy_static! |
std::sync::OnceLock |
1.70 |
once_cell::Lazy |
std::sync::LazyLock |
1.80 |
std::sync::mpsc |
crossbeam::channel |
- |
std::sync::Mutex |
parking_lot::Mutex |
- |
failure/error-chain |
thiserror/anyhow |
- |
try!() |
? operator |
2018 |
Clippy Configuration
[package]
edition = "2024"
rust-version = "1.85"
[lints.rust]
unsafe_code = "warn"
[lints.clippy]
all = "warn"
pedantic = "warn"
Common Clippy Lints
| Lint | Description |
|---|---|
clippy::all |
Enable all warnings |
clippy::pedantic |
Stricter checks |
clippy::unwrap_used |
Avoid unwrap |
clippy::expect_used |
Prefer expect |
clippy::clone_on_ref_ptr |
Avoid cloning Arc |
Formatting (rustfmt)
# Use default config
rustfmt src/lib.rs
# Check formatting
rustfmt --check src/lib.rs
# Config file: .rustfmt.toml
max_width = 100
tab_spaces = 4
edition = "2024"
Documentation Guidelines
/// Module documentation
//! This module handles user authentication...
/// Struct documentation
///
/// # Examples
/// ```
/// let user = User::new("name");
/// ```
pub struct User { ... }
/// Method documentation
///
/// # Arguments
///
/// * `name` - User name
///
/// # Returns
///
/// Initialized user instance
///
/// # Panics
///
/// Panics when name is empty
pub fn new(name: &str) -> Self { ... }
Workflow
Step 1: Name Things Properly
Choosing a name?
→ Function/variable? snake_case
→ Type/trait? CamelCase
→ Constant? SCREAMING_SNAKE_CASE
→ Conversion method?
- Cheap view? as_foo()
- Expensive? to_foo()
- Consuming? into_foo()
Step 2: Format Code
# Run rustfmt
cargo fmt
# Check formatting in CI
cargo fmt --check
# Fix clippy warnings
cargo clippy --fix
Step 3: Review Idioms
Check:
→ No unnecessary clone()
→ Use ? not unwrap()
→ &str in function parameters
→ Iterator methods not index loops
→ Meaningful error types
Quick Reference
Naming: snake_case (fn/var), CamelCase (type), SCREAMING_SNAKE_CASE (const)
Format: rustfmt (just use it)
Docs: /// for public items, //! for module docs
Lint: #![warn(clippy::all)]
Review Checklist
When reviewing code:
- Naming follows Rust conventions
- Using
?instead ofunwrap() - Avoiding unnecessary
clone() -
unsafeblocks have SAFETY comments - Public APIs have doc comments
- Ran
cargo clippy - Ran
cargo fmt - No
get_prefix on accessor methods - Conversion methods named correctly (as/to/into)
- String parameters use
&strwhen possible
Verification Commands
# Format check
cargo fmt --check
# Lint check
cargo clippy -- -D warnings
# Documentation check
cargo doc --no-deps --open
# Run tests
cargo test
# Check naming conventions
cargo clippy -- -W clippy::wrong_self_convention
Common Pitfalls
1. Wrong Method Naming
Symptom: Clippy warning wrong_self_convention
// ❌ Bad: unnecessary get_ prefix
impl User {
fn get_name(&self) -> &str { &self.name }
}
// ✅ Good: direct accessor
impl User {
fn name(&self) -> &str { &self.name }
}
2. String Type Misuse
Symptom: Unnecessary allocations
// ❌ Bad: forces allocation
fn greet(name: String) {
println!("Hello, {}", name);
}
// ✅ Good: accepts borrowed or owned
fn greet(name: &str) {
println!("Hello, {}", name);
}
// Both work now:
greet("Alice"); // &str
greet(&owned_string); // &String → &str
3. Index Loops
Symptom: Less idiomatic, error-prone
// ❌ Bad: manual indexing
for i in 0..items.len() {
println!("{}: {}", i, items[i]);
}
// ✅ Good: iterator
for item in &items {
println!("{}", item);
}
// ✅ Good: with index
for (i, item) in items.iter().enumerate() {
println!("{}: {}", i, item);
}
Related Skills
- rust-anti-pattern - What not to do
- rust-error - Error handling patterns
- rust-performance - Performance idioms
- rust-async - Async conventions
- rust-unsafe - SAFETY comment style
Localized Reference
- Chinese version: SKILL_ZH.md - 完整中文版本,包含所有内容
Weekly Installs
8
Repository
huiali/rust-skillsGitHub Stars
20
First Seen
Jan 30, 2026
Security Audits
Installed on
gemini-cli6
claude-code5
github-copilot5
amp5
cline5
codex5