Comments
Comments are essential for making code readable and maintainable. Rust provides several types of comments for different purposes, from simple inline explanations to comprehensive API documentation.
Regular Comments
Rust supports two styles of regular comments that are ignored by the compiler:
Line Comments
Use //
for single-line comments:
fn main() {
// This is a line comment
let x = 5; // This is also a line comment
// You can have multiple lines
// of comments like this
let y = 10;
}
Block Comments
Use /* */
for block comments that can span multiple lines:
fn main() {
/*
* This is a block comment
* that spans multiple lines
*/
let x = 5;
/* This is also a block comment on one line */
let y = 10;
/*
Block comments can contain
any text, including code examples:
let example = "This won't be compiled";
println!("This is just in a comment");
*/
}
Nested Block Comments
Rust allows nested block comments, which is useful for commenting out code blocks:
fn main() {
/*
This is a comment
/* with a nested comment inside */
and this continues the outer comment
*/
/*
You can comment out entire functions:
fn disabled_function() {
/* even if they have their own comments */
println!("This won't run");
}
*/
}
Documentation Comments
Documentation comments are special comments that generate HTML documentation using rustdoc
.
Outer Documentation Comments
Use ///
to document the item that follows:
/// Calculates the area of a rectangle.
///
/// This function takes the width and height of a rectangle
/// and returns the area as a floating-point number.
///
/// # Arguments
///
/// * `width` - The width of the rectangle in units
/// * `height` - The height of the rectangle in units
///
/// # Examples
///
/// ```
/// let area = calculate_area(10.0, 5.0);
/// assert_eq!(area, 50.0);
/// ```
///
/// # Panics
///
/// This function will panic if either width or height is negative.
fn calculate_area(width: f64, height: f64) -> f64 {
assert!(width >= 0.0, "Width must be non-negative");
assert!(height >= 0.0, "Height must be non-negative");
width * height
}
Inner Documentation Comments
Use //!
to document the containing item (like a module or crate):
//! # My Math Library
//!
//! This library provides basic mathematical operations
//! for geometric calculations.
//!
//! ## Features
//!
//! - Rectangle area calculation
//! - Circle area calculation
//! - Perimeter calculations
//!
//! ## Usage
//!
//! ```
//! use my_math_lib::calculate_area;
//!
//! let area = calculate_area(10.0, 5.0);
//! println!("Area: {}", area);
//! ```
/// Calculate the area of a rectangle
fn calculate_area(width: f64, height: f64) -> f64 {
width * height
}
Documentation Sections
Rust documentation comments support several standard sections:
Common Sections
/// A struct representing a bank account.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let account = BankAccount::new("123456", 1000.0);
/// assert_eq!(account.balance(), 1000.0);
/// ```
///
/// # Panics
///
/// The `new` function panics if the initial balance is negative.
///
/// # Errors
///
/// This type doesn't return errors directly, but methods may
/// return `Result` types for operations that can fail.
///
/// # Safety
///
/// This struct is safe to use in single-threaded contexts.
/// For multi-threaded usage, wrap in appropriate synchronization.
pub struct BankAccount {
account_number: String,
balance: f64,
}
impl BankAccount {
/// Creates a new bank account.
///
/// # Arguments
///
/// * `account_number` - A string slice that holds the account number
/// * `initial_balance` - The starting balance for the account
///
/// # Examples
///
/// ```
/// let account = BankAccount::new("123456", 1000.0);
/// ```
///
/// # Panics
///
/// Panics if `initial_balance` is negative.
pub fn new(account_number: &str, initial_balance: f64) -> BankAccount {
assert!(initial_balance >= 0.0, "Initial balance cannot be negative");
BankAccount {
account_number: account_number.to_string(),
balance: initial_balance,
}
}
/// Returns the current balance.
///
/// # Examples
///
/// ```
/// let account = BankAccount::new("123456", 1000.0);
/// assert_eq!(account.balance(), 1000.0);
/// ```
pub fn balance(&self) -> f64 {
self.balance
}
}
Code Examples in Documentation
Basic Code Examples
/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}
Code Examples That Don't Run
Use no_run
for examples that compile but shouldn't be executed:
/// Connects to a database.
///
/// # Examples
///
/// ```no_run
/// let connection = connect_to_database("localhost:5432");
/// ```
fn connect_to_database(url: &str) -> DatabaseConnection {
// Implementation would go here
DatabaseConnection::new(url)
}
Code Examples That Should Panic
Use should_panic
for examples that demonstrate panic behavior:
/// Divides two numbers.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let result = divide(10.0, 2.0);
/// assert_eq!(result, 5.0);
/// ```
///
/// This will panic:
///
/// ```should_panic
/// let result = divide(10.0, 0.0); // Panics!
/// ```
fn divide(a: f64, b: f64) -> f64 {
if b == 0.0 {
panic!("Cannot divide by zero");
}
a / b
}
Ignoring Code Examples
Use ignore
for examples that shouldn't be tested:
/// This function demonstrates complex async patterns.
///
/// # Examples
///
/// ```ignore
/// // This example requires async runtime setup
/// async fn example() {
/// let result = complex_async_operation().await;
/// println!("Result: {}", result);
/// }
/// ```
async fn complex_async_operation() -> String {
"result".to_string()
}
Module-Level Documentation
Document modules using inner documentation comments:
//! # Geometry Module
//!
//! This module provides utilities for geometric calculations.
//!
//! ## Overview
//!
//! The module includes functions and structs for working with
//! basic geometric shapes like rectangles, circles, and triangles.
//!
//! ## Examples
//!
//! ```
//! use geometry::{Rectangle, Circle};
//!
//! let rect = Rectangle::new(10.0, 5.0);
//! let area = rect.area();
//!
//! let circle = Circle::new(3.0);
//! let circumference = circle.circumference();
//! ```
use std::f64::consts::PI;
/// A rectangle with width and height.
pub struct Rectangle {
width: f64,
height: f64,
}
/// A circle with a radius.
pub struct Circle {
radius: f64,
}
Documenting Different Items
Structs and Fields
/// Represents a point in 2D space.
///
/// # Examples
///
/// ```
/// let point = Point { x: 1.0, y: 2.0 };
/// let distance = point.distance_from_origin();
/// ```
#[derive(Debug, Clone)]
pub struct Point {
/// The x-coordinate of the point
pub x: f64,
/// The y-coordinate of the point
pub y: f64,
}
impl Point {
/// Creates a new point at the origin.
///
/// # Examples
///
/// ```
/// let origin = Point::origin();
/// assert_eq!(origin.x, 0.0);
/// assert_eq!(origin.y, 0.0);
/// ```
pub fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
}
Enums and Variants
/// Represents different types of errors that can occur.
///
/// This enum categorizes errors into different types for
/// better error handling and user experience.
#[derive(Debug)]
pub enum MyError {
/// An I/O error occurred while reading or writing data.
///
/// This variant wraps the underlying `std::io::Error`.
Io(std::io::Error),
/// A parsing error occurred while processing input.
///
/// The string contains a description of what couldn't be parsed.
Parse(String),
/// A network error occurred during communication.
///
/// This includes timeouts, connection failures, etc.
Network {
/// The error message describing what went wrong
message: String,
/// The error code, if available
code: Option<u32>,
},
}
Constants and Statics
/// The maximum number of retries for network operations.
///
/// This constant defines how many times a network operation
/// will be attempted before giving up.
pub const MAX_RETRIES: u32 = 3;
/// Global configuration loaded at startup.
///
/// This static variable holds configuration that is loaded
/// once when the program starts and remains constant.
pub static GLOBAL_CONFIG: Config = Config::default();
Generating Documentation
Using rustdoc
Generate HTML documentation from your comments:
# Generate documentation for your crate
cargo doc
# Generate and open documentation in browser
cargo doc --open
# Include private items in documentation
cargo doc --document-private-items
Documentation Tests
Documentation examples are automatically tested:
# Run documentation tests
cargo test --doc
# Run all tests including doc tests
cargo test
Comment Best Practices
1. Explain Why, Not What
// Bad: Explains what the code does (obvious)
let x = 5; // Set x to 5
// Good: Explains why this value is chosen
let timeout_seconds = 30; // 30 seconds balances responsiveness with server load
2. Update Comments When Code Changes
// Bad: Comment doesn't match the code
// Return true if number is even
fn is_odd(n: i32) -> bool {
n % 2 != 0
}
// Good: Comment matches the implementation
// Return true if number is odd
fn is_odd(n: i32) -> bool {
n % 2 != 0
}
3. Use TODO and FIXME Comments
fn calculate_tax(income: f64) -> f64 {
// TODO: Add support for different tax brackets
// FIXME: This doesn't handle edge case where income is negative
income * 0.2
}
4. Document Complex Algorithms
/// Implements the quicksort algorithm for sorting integers.
///
/// This is a divide-and-conquer algorithm that works by selecting
/// a 'pivot' element and partitioning the other elements into two
/// sub-arrays according to whether they are less than or greater
/// than the pivot.
///
/// # Time Complexity
///
/// - Average case: O(n log n)
/// - Worst case: O(n²)
///
/// # Examples
///
/// ```
/// let mut numbers = vec![64, 34, 25, 12, 22, 11, 90];
/// quicksort(&mut numbers);
/// assert_eq!(numbers, vec![11, 12, 22, 25, 34, 64, 90]);
/// ```
fn quicksort(arr: &mut [i32]) {
if arr.len() <= 1 {
return;
}
// Choose the last element as pivot
let pivot_index = partition(arr);
// Recursively sort elements before and after partition
quicksort(&mut arr[0..pivot_index]);
quicksort(&mut arr[pivot_index + 1..]);
}
5. Link to Related Items
/// Represents a user in the system.
///
/// See also: [`UserManager`] for user management operations,
/// and [`UserPermissions`] for handling user permissions.
///
/// [`UserManager`]: crate::manager::UserManager
/// [`UserPermissions`]: crate::permissions::UserPermissions
pub struct User {
pub id: u64,
pub name: String,
}
Advanced Documentation Features
Markdown Support
Documentation comments support Markdown formatting:
/// # This is a large heading
///
/// ## This is a smaller heading
///
/// You can use **bold text** and *italic text*.
///
/// - Bullet points work
/// - As do numbered lists:
/// 1. First item
/// 2. Second item
///
/// You can also include `inline code` and links: [Rust Book](https://doc.rust-lang.org/book/)
///
/// | Feature | Supported |
/// |---------|-----------|
/// | Tables | Yes |
/// | Images | Yes |
fn documented_function() {}
Cross-References
Link to other items in your documentation:
/// A helper struct that works with [`MyStruct`].
///
/// See the [`process`] method for the main functionality.
///
/// [`MyStruct`]: crate::MyStruct
/// [`process`]: crate::MyStruct::process
pub struct Helper;
/// The main struct in this crate.
///
/// Use [`Helper`] for additional functionality.
pub struct MyStruct;
impl MyStruct {
/// Process data using this struct.
///
/// This method is referenced from [`Helper`].
pub fn process(&self) {
// Implementation
}
}
Effective commenting is crucial for maintaining readable and maintainable Rust code. Use regular comments for implementation details and documentation comments for public APIs that others will use.