Backend7 min read

Rust Programming

Learn Rust - ownership and borrowing, lifetimes, async/await with Tokio, error handling with Result, and building CLI and web APIs.

RustSystems ProgrammingMemory SafetyWebAssemblyPerformance

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

  1. 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.
  2. 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.
  3. 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.
  4. Build a web API with AxumAxum is the idiomatic Rust web framework, built on Tokio. Type-safe extractors, compile-time routing, and excellent performance.
  5. 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

Input

Output

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

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.