Rust Coding Standards
1. Follow Rust Style Guidelines
The Rust community has a set of widely accepted style conventions. Use rustfmt to automatically format your code.
Good Practice:
- Install rustfmt:
rustup component add rustfmt
- Format your code:
cargo fmt
Example:
Unformatted code:
fn main() {println!("Hello, world!");}
Formatted code:
fn main() {
println!("Hello, world!");
}
2. Use Meaningful Names
Choose variable, function, and type names that clearly describe their purpose. Avoid abbreviations or single-letter names unless they are universally understood.
Example:
Bad Practice:
fn calc(x: i32, y: i32) -> i32 {
x + y
}
Good Practice:
fn calculate_sum(number1: i32, number2: i32) -> i32 {
number1 + number2
}
3. Write Small and Focused Functions
Each function should perform a single task. Break down complex logic into smaller functions for better readability and reusability.
Example:
Bad Practice:
fn process_data(data: &str) {
let uppercase = data.to_uppercase();
let trimmed = uppercase.trim();
println!("{}", trimmed);
}
Good Practice:
fn convert_to_uppercase(data: &str) -> String {
data.to_uppercase()
}
fn trim_string(data: &str) -> &str {
data.trim()
}
fn main() {
let data = " rust coding ";
let uppercase = convert_to_uppercase(data);
let trimmed = trim_string(&uppercase);
println!("{}", trimmed);
}
4. Use match Instead of Multiple if-else
Leverage Rust’s powerful pattern matching with match to handle conditions more cleanly.
Example:
Bad Practice:
fn get_status_code(code: i32) -> &'static str {
if code == 200 {
"OK"
} else if code == 404 {
"Not Found"
} else {
"Unknown"
}
}
Good Practice:
fn get_status_code(code: i32) -> &'static str {
match code {
200 => "OK",
404 => "Not Found",
_ => "Unknown",
}
}
5. Handle Errors Gracefully
Use Rust’s Result and Option types to manage errors and avoid panics.
Example:
use std::fs::File;
fn open_file(filename: &str) -> Result<File, std::io::Error> {
File::open(filename)
}
fn main() {
match open_file("data.txt") {
Ok(file) => println!("File opened successfully: {:?}", file),
Err(error) => println!("Error opening file: {}", error),
}
}
6. Use Iterators and Closures
Prefer iterators and closures for looping and data transformation.
Example:
Bad Practice:
let numbers = vec![1, 2, 3, 4];
let mut sum = 0;
for number in numbers {
sum += number;
}
Good Practice:
let numbers = vec![1, 2, 3, 4];
let sum: i32 = numbers.iter().sum();
7. Write Comprehensive Tests
Include unit and integration tests to validate your code. Use the #[test] attribute for unit tests.
Example:
#[cfg(test)]
mod tests {
#[test]
fn test_addition() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
Run tests using:
cargo test
8. Document Your Code
Use /// for documentation comments to describe the purpose of functions, modules, and structs.
Example:
/// Adds two numbers and returns the result.
fn add(a: i32, b: i32) -> i32 {
a + b
}
Generate documentation with:
cargo doc --open
9. Use Clippy for Linting
Clippy is a linter that checks for common mistakes and suggests improvements.
Install Clippy:
rustup component add clippy
Run Clippy:
cargo clippy
10. Avoid Unsafe Code
Use the unsafe keyword only when absolutely necessary and document why it’s required.
Example:
unsafe {
let raw_pointer = &10 as *const i32;
println!("Raw pointer value: {}", *raw_pointer);
}
11. Use Constants and Enums for Fixed Values
Avoid using magic numbers in your code. Use constants and enums for better readability.
Example:
Bad Practice:
if age > 18 {
println!("Adult");
}
Good Practice:
const ADULT_AGE: i32 = 18;
if age > ADULT_AGE {
println!("Adult");
}
12. Modularize Your Code
Split your code into modules for better organization and reuse.
Example:
// main.rs
mod math;
fn main() {
let result = math::add(2, 3);
println!("Sum: {}", result);
}
// math.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}