Rust Core Standards
Ownership & Borrowing
Ownership Rules:
- Each value has exactly one owner
- Value dropped when owner goes out of scope
- Do: Move or clone explicitly when needed
- Don't: Fight the borrow checker with unsafe
Borrowing:
- Immutable:
&T- multiple allowed - Mutable:
&mut T- only one, no immutable refs - Rule: References must not outlive data
- Immutable:
Error Handling
// Use Result for recoverable errors
fn parse_config(path: &str) -> Result<Config, ConfigError> {
let content = std::fs::read_to_string(path)?;
toml::from_str(&content).map_err(ConfigError::Parse)
}
// Use Option for optional values
fn find_user(id: u64) -> Option<User> { /* ... */ }
// Custom error types
#[derive(Debug, thiserror::Error)]
enum AppError {
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("not found: {0}")]
NotFound(String),
}
Patterns:
?operator for propagationthiserrorfor library errorsanyhowfor application errors- Never:
unwrap()in production code (useexpectwith context)
Async/Await
- Runtime: Tokio for production
- Rule: Async functions return
Future, need executor
#[tokio::main]
async fn main() {
let result = fetch_data().await;
}
async fn fetch_data() -> Result<Data, Error> {
let response = reqwest::get("https://api.example.com").await?;
response.json().await.map_err(Into::into)
}
Concurrency Patterns:
tokio::spawnfor background taskstokio::select!for racing futurestokio::sync::Mutexfor shared async state- Warning:
std::sync::Mutexblocks; usetokio::syncin async
Traits & Generics
// Define trait bounds
fn process<T: Serialize + Debug>(item: T) -> String { /* ... */ }
// Impl blocks
impl<T: Clone> Container<T> {
fn duplicate(&self) -> Self { /* ... */ }
}
// Associated types for clarity
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
Best Practices:
- Prefer
impl Traitfor return types - Use
whereclauses for complex bounds #[derive]for common traits:Debug, Clone, PartialEq
Project Structure
my-project/
├── Cargo.toml
├── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Library root (optional)
│ ├── config.rs # Configuration
│ ├── error.rs # Error types
│ ├── handlers/ # Request handlers
│ │ └── mod.rs
│ └── models/ # Data structures
│ └── mod.rs
└── tests/
└── integration.rs # Integration tests
Conventions:
mod.rsfor module rootspubonly what's needed- Re-export with
pub useat module root
Testing
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
let result = parse("valid");
assert_eq!(result, Ok(Expected));
}
#[tokio::test]
async fn test_async_fn() {
let data = fetch().await;
assert!(data.is_ok());
}
}
- Unit tests in same file with
#[cfg(test)] - Integration tests in
tests/directory - Use
mockallfor mocking traits
Security
- Input Validation: Validate all external input before processing
- SQL Injection: Use parameterized queries (sqlx, diesel)
- Dependencies: Run
cargo auditregularly - Unsafe: Minimize
unsafeblocks, document invariants - Secrets: Use
secrecycrate for sensitive data in memory