Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
72: Documentation with rustdoc
I've spent way too many hours in my career staring at a library's source code because the documentation was either non-existent or out of date. In Rust, we have a massive advantage: the documentation is built directly into the toolchain. If you write it in the code, it stays with the code.
Let's walk through how I actually set this up. I'm working on a small PacketParser for a custom binary protocol. It's a simple thing—just taking a slice of bytes and turning it into a structured object—but it's exactly the kind of logic where a developer (including future me) will forget why a specific offset was chosen.
Wait, where did my comments go?
Naturally, I started by writing standard comments. I figured I'd just explain the parse method and then run the doc generator.
pub struct Packet {
pub id: u32,
pub payload: Vec,
}
impl Packet {
// This takes a byte slice and attempts to parse it into a Packet.
// It returns None if the slice is too short.
pub fn parse(data: &[u8]) -> Option {
if data.len() < 4 { return None; }
let id = u32::from_be_bytes(data[0..4].try_into().unwrap());
Some(Packet { id, payload: data[4..].to_vec() })
}
}
I ran cargo doc --open. The browser popped up, I navigated to my crate, and... nothing. The parse method was listed, but there was no explanation. I realized I was using //, which are just internal comments for people reading the raw source. Rustdoc ignores those entirely.
I'll switch those to triple slashes ///. In Rust, that's the signal to the compiler: "This is documentation for the item immediately following this line."
impl Packet {
/// Parses a byte slice into a Packet.
///
/// Returns None if the slice is shorter than 4 bytes.
pub fn parse(data: &[u8]) -> Option {
// ... (implementation stays the same)
}
}
Running cargo doc --open again, and there it is. It's clean, it's rendered, and it's actually visible in the browser. Simple, but it's a common "gotcha" when you first move from other languages.
Giving the module some context
The function documentation is great, but when I look at the top level of the generated page, it's a bit barren. I want to explain what this whole module does—why the protocol exists and how the packets are structured generally.
I tried putting /// at the very top of the file, but that felt wrong because there wasn't a specific item (like a struct) beneath it. Then I remembered the "inner doc" comment: //!. The exclamation mark tells Rustdoc that this comment applies to the containing item—in this case, the module itself.
//! # Packet Parser Module
//!
//! This module provides utilities for decoding the Custom Binary Protocol (CBP).
//! All packets start with a 4-byte big-endian identifier.
pub struct Packet {
/* ... */
}
Now, when I open the docs, the module landing page has a full header and a description. It turns the documentation from a "list of functions" into an actual manual.
Making the documentation actually useful
Plain text is fine, but as an engineer, I don't want to read a paragraph describing how to use a function; I want to see the code. Rustdoc supports Markdown, and more importantly, it supports code blocks that are actually tested.
I'm going to add an "Examples" section to the parse method. I'll use a triple-backtick code block. Here's the trick: if I don't mark the block as ignore or no_run, Cargo will actually compile and run this code during cargo test.
/// Parses a byte slice into a Packet.
///
/// # Examples
///
/// ```
/// use my_crate::Packet;
///
/// let data = [0, 0, 0, 1, 10, 20];
/// let packet = Packet::parse(&data).unwrap();
/// assert_eq!(packet.id, 1);
/// assert_eq!(packet.payload, vec![10, 20]);
/// ```
pub fn parse(data: &[u8]) -> Option {
// ...
}
I love this feature. It means my documentation can't rot. If I change the Packet struct and break the parse method, my tests will fail, and I'll be forced to update the documentation example. It's the only way to guarantee the docs actually work.
Connecting the dots
Lastly, my Packet struct has a payload field. I want to mention in the parse documentation that the payload is stored as a Vec<u8>, but I want to link directly to the Packet struct definition so the user can click through.
I discovered "intra-doc links." Instead of writing a complex HTML link, I can just put the type name in square brackets.
/// Parses a byte slice into a [`Packet`].
///
/// The resulting [`Packet`] will contain the remaining bytes as the payload.
pub fn parse(data: &[u8]) -> Option {
// ...
}
Now, the generated HTML has a clickable link that takes the user straight to the Packet struct documentation. It makes the whole API feel interconnected rather than a series of isolated pages.
📋 Practical Task
Documenting a Custom Buffer Wrapper
You have been handed a piece of code for a SecureBuffer—a wrapper that ensures data is wiped from memory when it's dropped. However, it is completely undocumented, making it dangerous for other team members to use.
Your task is to apply everything we've discussed to make this production-ready. Modify the code below to include:
- Module-level documentation (using
//!) explaining that this buffer is intended for sensitive cryptographic keys. - Struct-level documentation (using
///) explaining the purpose ofSecureBuffer. - Function-level documentation for the
newmethod, including a runnable "Examples" code block that demonstrates creating a buffer and accessing its data. - An intra-doc link within the
newmethod documentation that links back to theSecureBufferstruct.
pub struct SecureBuffer {
data: Vec,
}
impl SecureBuffer {
pub fn new(bytes: Vec) -> Self {
Self { data: bytes }
}
pub fn expose(&self) -> &[u8] {
&self.data
}
}
impl Drop for SecureBuffer {
fn drop(&mut self) {
// In a real scenario, we would zero out the memory here.
println!("Zeroing out sensitive memory...");
}
}There are no comments for now.