Best Practices
## Learning Objectives
- Write clean, idiomatic Rust
- Follow naming conventions
- Master cargo workflow
- Apply effective testing
- Remember borrowing rules
## Code Style
### Naming Conventions
```rust
// Modules: snake_case
mod network_module { }
// Structs, Enums, Traits: PascalCase
struct UserAccount { }
enum Color { Red, Green, Blue }
trait Summary { }
// Functions and Methods: snake_case
fn calculate_total() { }
fn process_data() { }
// Variables and Parameters: snake_case
let user_name = "Alice";
fn print_message(message: &str) { }
// Constants: SCREAMING_SNAKE_CASE
const MAX_RETRIES: u32 = 3;
const PI: f64 = 3.14159;
// Type Parameters: PascalCase
fn largest(list: &[T]) -> &T { }
// Enum Variants: PascalCase (like structs)
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
```
### Formatting
```rust
// Use rustfmt
// Run: rustfmt your_file.rs
// Line length: ~100 characters
// One statement per line
let name = "Alice";
let age = 30;
// Braces
if condition {
do_something();
} else {
do_other();
}
// Match arms
match x {
1 => println!("one"),
2 => {
let y = 2;
println!("{}", y);
}
_ => println!("other"),
}
```
## Cargo Workflow
### New Project
```bash
cargo new project_name
cd project_name
cargo run
```
### Dependencies
Add to Cargo.toml:
```toml
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8" # Version constraint
serde = { version = "1.0", features = ["derive"] } # With features
```
### Common Commands
| Command | Description |
|---------|-------------|
| cargo build | Build project |
| cargo run | Build and run |
| cargo test | Run tests |
| cargo doc | Generate docs |
| cargo check | Check without build |
| cargo build --release | Release build |
| cargo update | Update dependencies |
### Cargo.lock
```bash
# Commit Cargo.lock for reproducible builds
# Don't edit it manually
cargo update -p crate_name # Update single crate
```
## Ownership Best Practices
### Don't Fight the Borrow Checker
```rust
// Bad: unnecessary complexity
fn main() {
let mut data = vec![1, 2, 3];
let first = &data[0];
data.push(4); // Error: can't mutate while borrowed
println!("{}", first);
}
// Good: proper ordering
fn main() {
let mut data = vec![1, 2, 3];
println!("{}", data[0]); // Use directly
data.push(4); // Then mutate
}
```
### Use References for Reading
```rust
// Bad
fn print_sum(data: Vec) -> i32 {
let sum = data.iter().sum();
println!("{:?}", data);
sum
}
// Good: borrow
fn print_sum(data: &Vec) -> i32 {
let sum = data.iter().sum();
println!("{:?}", data);
sum
}
```
### Clone When Needed
```rust
// Don't clone unnecessarily
fn bad_function(data: String) -> usize {
data.len()
}
fn good_function(data: &str) -> usize {
data.len()
}
// When you need ownership
fn needs_owned(data: String) -> String {
// modify data
data
}
let s = String::from("hello");
let len = needs_owned(s.clone()); // Clone when needed
let len = needs_owned(s); // Or move, depending on usage
```
## Error Handling Best Practices
### Don't Use unwrap in Production
```rust
// Bad
fn read_config() -> Config {
Config::from_file("config.toml").unwrap()
}
// Good: propagate error
fn read_config() -> Result {
Config::from_file("config.toml")
}
// Good: provide default
fn read_config() -> Config {
Config::from_file("config.toml").unwrap_or_default()
}
```
### Use expect for Tests
```rust
#[cfg(test)]
mod tests {
#[test]
fn test_calculation() {
assert_eq!(calculate(2, 3), 5);
}
#[test]
#[should_panic(expected = "division by zero")]
fn test_division_by_zero() {
divide(1, 0);
}
}
```
## Testing
### Unit Tests
```rust
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_positive() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, -1), -2);
}
#[test]
#[should_panic(expected = "attempt to add with overflow")]
fn test_add_overflow() {
add(i32::MAX, 1);
}
}
```
### Integration Tests
Create tests in tests/ directory:
```rust
// tests/integration_test.rs
use my_crate;
#[test]
fn test_integration() {
assert_eq!(my_crate::add(1, 2), 3);
}
```
### Doc Tests
```rust
/// Adds two numbers.
///
/// # Examples
///
/// ```
/// assert_eq!(add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
```
## Documentation
### Document Modules
```rust
//! Network module
//!
//! Provides functionality for network communication.
pub mod client;
pub mod server;
```
### Document Functions
```rust
/// Calculates the sum of two numbers.
///
/// # Arguments
///
/// * `a` - First number
/// * `b` - Second number
///
/// # Returns
///
/// The sum of a and b
///
/// # Examples
///
/// ```
/// assert_eq!(add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
```
### Document Structs and Enums
```rust
/// Represents a user in the system
pub struct User {
/// User's unique identifier
pub id: u64,
/// User's display name
pub name: String,
}
/// Represents a message type
pub enum Message {
/// Quit the application
Quit,
/// Move to position
Move { x: i32, y: i32 },
}
```
## Performance Tips
### Avoid Unnecessary Allocations
```rust
// Bad: multiple allocations
let mut s = String::new();
for word in &words {
s.push_str(word);
s.push(' ');
}
// Good: preallocate or use join
let s = words.join(" ");
// Or
let mut s = String::with_capacity(total_len);
for word in &words {
s.push_str(word);
s.push(' ');
}
```
### Use References in Loops
```rust
// Bad: copies in each iteration
for word in words.clone() { }
// Good: borrows
for word in &words { }
```
### Prefer Stack Allocation
```rust
// Bad: heap allocation
let arr = Box::new([1, 2, 3]);
// Good: stack allocation
let arr = [1, 2, 3];
```
## Iterators
### Use Iterators Instead of Loops
```rust
let numbers = vec![1, 2, 3, 4, 5];
// Instead of
let mut sum = 0;
for n in &numbers {
sum += n;
}
// Prefer
let sum: i32 = numbers.iter().sum();
// Instead of
let mut doubled = Vec::new();
for n in &numbers {
doubled.push(n * 2);
}
// Prefer
let doubled: Vec = numbers.iter().map(|n| n * 2).collect();
```
## Smart Pointers
### When to Use Box
```rust
// Recursive types
enum List {
Cons(T, Box
- >),
Nil,
}
// Trait objects
fn print_debug(item: &dyn Debug) {
println!("{:?}", item);
}
```
## Summary
- Follow Rust naming conventions (snake_case, PascalCase)
- Use `cargo fmt` and `clippy` for style
- Don't use `unwrap()` in production code
- Propagate errors with `?` or return Result
- Write tests: unit tests, integration tests, doc tests
- Document public APIs
- Prefer iterators over loops when appropriate
- Clone intentionally, not by accident
- Use `Box
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →