First Encounter: Running Safe Code: Code Companion¶
Reference code for the First Encounter: Running Safe Code lecture. Sections correspond to the lecture document.
Section 1: The Anatomy of a Rust Entry Point¶
fn main() {
// The simplest entry point: no arguments, returns unit type ()
// Rust's runtime has already initialized safely before this runs
println!("=== Buffer Overflow Prevention in Rust ===\n");
// ... demonstration code follows
}
The implicit return type is () (unit). For error-handling entry points, you could use fn main() -> Result<(), E> or fn main() -> ExitCode.
Section 2: Module Imports and the Crate Ecosystem¶
// Import the buffer_overflow module from our crate
use rust_memory_safety_examples::buffer_overflow;
fn main() {
// Call functions using the module path
buffer_overflow::safe_array_access();
// ^-- module ^-- function
buffer_overflow::safe_string_handling();
buffer_overflow::compare_c_vs_rust();
}
The :: path separator navigates the module hierarchy. The use statement brings the module into scope, allowing buffer_overflow::function() instead of the full path rust_memory_safety_examples::buffer_overflow::function().
Section 3: Documentation Comments and Intent¶
//! Buffer overflow prevention example
// ^-- The `!` makes this an INNER doc comment
// It documents the containing item (the whole file)
// Regular comments use just //
// Doc comments for items below them use ///
use rust_memory_safety_examples::buffer_overflow;
Inner doc comments (//!) become module-level documentation when you run cargo doc. Outer doc comments (///) document the item that follows them.
Section 4: Sequential Demonstration Structure¶
fn main() {
// Header with visual separator
println!("=== Buffer Overflow Prevention in Rust ===\n");
// ^-- \n adds newline
// Section 1: Basic array safety
println!("1. Safe Array Access");
buffer_overflow::safe_array_access();
// Section 2: Heap-allocated string safety
println!("\n2. Safe String Handling");
// ^-- leading newline separates from previous output
buffer_overflow::safe_string_handling();
// Section 3: Cross-language comparison
println!("\n3. C vs Rust Comparison");
buffer_overflow::compare_c_vs_rust();
// ... summary follows
}
The println! macro (note the !) accepts format strings with escape sequences. The numbered progression creates a teachable output structure.
Section 5: Function Calls as Safety Boundaries¶
// These functions return () - they exist for side effects
buffer_overflow::safe_array_access(); // -> ()
buffer_overflow::safe_string_handling(); // -> ()
buffer_overflow::compare_c_vs_rust(); // -> ()
// Each call is a contained experiment:
// - If bounds are violated, Rust panics safely
// - No memory corruption can occur
// - No undefined behavior possible
Functions returning () are common in example code. Production code typically returns Result<T, E> for testability and error propagation via the ? operator.
Section 6: The Summary Output Pattern¶
println!("\n=== Key Takeaways ===");
// Unicode works without imports - Rust strings are UTF-8 by default
println!("✓ Rust prevents buffer overflows at compile time and runtime");
// ^-- Unicode checkmark: U+2713
println!("✓ Bounds checking is automatic and cannot be bypassed");
println!("✓ No undefined behavior - panics are safe and recoverable");
println!("✓ Strings automatically resize - no fixed buffer vulnerabilities");
Rust's String and &str types guarantee valid UTF-8 encoding. The compiler enforces this invariant, making string handling both safe and Unicode-aware by default.
Section 7: Connecting to the Library¶
Project structure:
rust-memory-safety-examples/
├── Cargo.toml
├── src/
│ ├── lib.rs <- Library root, exports modules
│ └── buffer_overflow.rs <- Contains the actual demonstrations
└── examples/
└── buffer_overflow_prevention.rs <- THIS FILE (you are here)
// In lib.rs (not shown), something like:
pub mod buffer_overflow;
// This example links against the library via Cargo:
// $ cargo run --example buffer_overflow_prevention
The examples/ directory has special meaning to Cargo. Files here compile as separate binaries that depend on the main library crate.
Section 8: What Running This Code Teaches¶
// The complete file - notice what's ABSENT:
//! Buffer overflow prevention example
use rust_memory_safety_examples::buffer_overflow;
fn main() {
println!("=== Buffer Overflow Prevention in Rust ===\n");
println!("1. Safe Array Access");
buffer_overflow::safe_array_access();
println!("\n2. Safe String Handling");
buffer_overflow::safe_string_handling();
println!("\n3. C vs Rust Comparison");
buffer_overflow::compare_c_vs_rust();
println!("\n=== Key Takeaways ===");
println!("✓ Rust prevents buffer overflows at compile time and runtime");
println!("✓ Bounds checking is automatic and cannot be bypassed");
println!("✓ No undefined behavior - panics are safe and recoverable");
println!("✓ Strings automatically resize - no fixed buffer vulnerabilities");
}
// What you DON'T see:
// - No manual memory allocation
// - No null checks
// - No buffer size tracking
// - No cleanup code
// - No error-prone pointer arithmetic
The absence of defensive code is the point. Rust's type system and ownership model handle these concerns, letting you focus on logic rather than memory management.
Quick Reference¶
| Element | Syntax | Purpose |
|---|---|---|
| Entry point | fn main() |
Program execution starts here |
| Module import | use crate::module |
Brings module into scope |
| Path separator | :: |
Navigates module hierarchy |
| Inner doc comment | //! |
Documents containing item |
| Outer doc comment | /// |
Documents following item |
| Macro call | name!() |
Compile-time code expansion |
| Unit type | () |
Empty return / no value |
Common Entry Point Signatures¶
fn main() { } // Simple, no error handling
fn main() -> Result<(), Error> { } // Can use ? operator
fn main() -> ExitCode { } // Explicit exit codes (Rust 1.61+)