← Rust EnglishChapter 13 of 13

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` for recursive types and trait objects

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →