Rust Programming
Learn Rust - ownership and borrowing, lifetimes, async/await with Tokio, error handling with Result, and building CLI and web APIs.
skill: Rust Programming category: Backend tags: [Rust, Systems Programming, Memory Safety, WebAssembly, Performance] readingTime: 7
Rust's ownership model is simultaneously its most intimidating and most valuable feature. It guarantees memory safety at compile time - no null pointer dereferences, no use-after-free, no data races - without a garbage collector. Learn the ownership rules thoroughly and the borrow checker becomes a helpful compiler, not an adversary.
Workflow
- Understand ownership, borrowing, and lifetimesRust's ownership model is why it's memory-safe without GC. Every value has exactly one owner; when the owner goes out of scope, the value is dropped. References (borrows) let you use values without taking ownership.
- Handle errors with Result and the ? operatorRust has no exceptions. Errors are values - Result<T, E> is either Ok(value) or Err(error). The ? operator propagates errors up the call stack without boilerplate.
- Write async code with TokioTokio is Rust's async runtime - it enables non-blocking I/O for high-performance web servers and network applications without manual threads.
- Build a web API with AxumAxum is the idiomatic Rust web framework, built on Tokio. Type-safe extractors, compile-time routing, and excellent performance.
- Use traits for polymorphismTraits are Rust's mechanism for polymorphism - define shared behavior that multiple types implement. No inheritance, no virtual dispatch overhead (unless using dyn Trait).
Output Format
Rust: ownership/borrow rules, Result<?> error propagation with thiserror, tokio::join! for concurrent async, Axum typed extractors, and trait-based polymorphism.
Ownership: single owner, & for borrow, &mut for exclusive write
Error: Result<T, E> + thiserror::Error derive + ? propagation
Async: #[tokio::main] + async fn + await + tokio::spawn
Axum: Router::new().route() + State + Json extractor
Traits: trait Foo { fn bar(&self); } impl Foo for MyType { ... }Examples
Edge Cases & Gotchas
- Clone vs. Copy - Types that implement Copy (integers, booleans) are implicitly copied on assignment - no move. Types like String require explicit .clone() for a deep copy. Clone can be expensive for large data structures.
- Lifetime annotations - Lifetime annotations (&'a str) tell the compiler how long references are valid. Required when the compiler can't infer lifetimes. Most Rust code doesn't need explicit lifetimes - only when returning references from functions or storing references in structs.
- Send and Sync traits - Send means a type is safe to send to another thread; Sync means safe to share between threads. Types that don't implement these can't be used across threads - the borrow checker enforces this at compile time.
- RefCell and interior mutability - RefCell<T> enables mutation behind an immutable reference (interior mutability) - borrow checking moves to runtime. Use sparingly (RefCell<T> panics on double mutable borrow); prefer Arc<Mutex<T>> for thread-safe shared state.
When to Break These Rules
- Use Go instead of Rust for teams prioritizing developer productivity over maximum performance - Rust's learning curve (borrow checker) adds weeks of onboarding per developer.
- Use Rust for performance-critical paths (parsers, encoders, game engines) but Go or TypeScript for most application-level code where the performance difference doesn't matter.
Reference Files
https://doc.rust-lang.org/book/https://github.com/tokio-rs/axum