Build Redis from Scratch in Rust — Part 3: Benchmarks and Production Features

In Part 1, we built a TCP server with SET, GET, and DEL. In Part 2, we added expiry, persistence, and pub/sub. Now we add more data types, benchmark our implementation, and make it production-ready. In this final part, we add: INCR — atomic integer increment LPUSH, LPOP, LRANGE — list operations Benchmarks against real Redis Graceful shutdown with signal handling Better error handling throughout Adding INCR INCR atomically increments a number stored at a key. If the key does not exist, it starts at 0. If the value is not a number, it returns an error. This is how real Redis counters work. ...

July 24, 2026 · 12 min

Build Redis from Scratch in Rust — Part 2: Expiry, Persistence, and Pub/Sub

In Part 1, we built a TCP server that speaks the Redis protocol. We implemented SET, GET, and DEL commands with in-memory storage. But real Redis has many more features. In this part, we add three important features: Key expiry — keys that delete themselves after a timeout Persistence — saving data to disk so it survives restarts Pub/Sub — publish and subscribe messaging between clients Key Expiry In real Redis, you can set a key with an expiration time. After that time, the key disappears. This is useful for caches, sessions, and rate limiting. ...

July 24, 2026 · 11 min

Build Redis from Scratch in Rust — Part 1: TCP Server and Commands

Have you ever wondered how Redis works under the hood? In this mini-series, we build a Redis clone from scratch in Rust. No magic. Just a TCP server, a protocol parser, and a HashMap. By the end of this series, you will have a working key-value store that speaks the real Redis protocol. You can connect to it with redis-cli and run commands. This is Part 1. We will build: ...

July 23, 2026 · 9 min

Rust Developer Roadmap 2026

Rust has been the most loved programming language for over a decade. In 2026, it is also one of the most practical. Companies like Microsoft, Google, Amazon, and Cloudflare use Rust in production. The demand for Rust developers keeps growing, but the supply is small. That means higher salaries and more opportunities. This roadmap takes you from zero Rust knowledge to job-ready. It follows the same order as our 28-article Rust tutorial series, with clear milestones and time estimates. ...

July 22, 2026 · 10 min

Backend Developer Roadmap 2026 — Complete Guide

Backend development is the engine behind every app and website. In 2026, the backend landscape is more exciting than ever. New frameworks, better tooling, and AI assistants make it faster to build production-ready APIs. This roadmap covers everything you need to become a backend developer. It includes language choices, databases, deployment, and a realistic timeline. Follow the stages in order for the best results. Why Backend Development in 2026? Every mobile app, website, and service needs a backend. The demand is constant and growing. Here is what makes 2026 special: ...

July 22, 2026 · 10 min

Python vs Rust 2026 — When to Use Which

Python and Rust could not be more different. Python is the world’s most popular language — easy, flexible, everywhere. Rust is the most loved language — fast, safe, precise. But here is the interesting part: in 2026, they are not competitors. They are partners. Many of the fastest Python tools are actually written in Rust underneath. This guide will help you understand when to use each language and when to use them together. ...

July 18, 2026 · 10 min

Rust vs Go 2026 — Performance vs Simplicity

Rust and Go are two of the fastest-growing programming languages. Both were designed to solve real problems with existing languages. But they made very different tradeoffs. Go chose simplicity. Fast compilation, easy concurrency, minimal syntax. Ship code quickly. Rust chose safety and performance. Zero-cost abstractions, memory safety without garbage collection, fearless concurrency. Ship correct code. This guide compares them in depth so you can choose the right tool for your project. ...

July 15, 2026 · 9 min

Rust Cheat Sheet 2026 — Syntax, Ownership, and Common Patterns

Bookmark this page. Use Ctrl+F (or Cmd+F on Mac) to find what you need. This cheat sheet covers Rust syntax, ownership, traits, error handling, and common patterns. Try examples at play.rust-lang.org. Last updated: March 2026 Variables and Types let name = "Alex"; // immutable (default) let mut count = 0; // mutable let age: i32 = 25; // explicit type const MAX: u32 = 100; // compile-time constant Type Description Example i8, i16, i32, i64, i128 Signed integers let x: i32 = -42; u8, u16, u32, u64, u128 Unsigned integers let x: u32 = 42; f32, f64 Floating point let x: f64 = 3.14; bool Boolean true, false char Unicode character 'A', '🚀' &str String slice (borrowed) "hello" String Owned string (heap) String::from("hello") () Unit type (void) fn do_thing() { } [T; N] Fixed array [1, 2, 3] Vec<T> Dynamic array vec![1, 2, 3] (T, U) Tuple (42, "hello") Option<T> Nullable value Some(42) or None Result<T, E> Success or error Ok(42) or Err("fail") Type Conversions let x: i32 = 42; let y: f64 = x as f64; // 42.0 let s: String = x.to_string(); // "42" let n: i32 = "42".parse().unwrap(); // 42 let n: i32 = "42".parse().unwrap_or(0); // 42 (or 0 on error) Strings // &str — string slice, borrowed, immutable let greeting: &str = "hello"; // String — owned, heap-allocated, growable let mut name = String::from("Alex"); name.push_str(" Smith"); // append name.push('!'); // append char // Conversions let s: String = "hello".to_string(); let s: &str = &name; // String → &str (auto-deref) // Common methods name.len() // byte length name.is_empty() // true if empty name.contains("Alex") // substring check name.starts_with("A") name.to_uppercase() name.to_lowercase() name.trim() // remove whitespace name.replace("Alex", "Sam") name.split(" ") // iterator of parts // Format let msg = format!("Hello {name}, age {age}"); Ownership and Borrowing // Ownership — each value has one owner let s1 = String::from("hello"); let s2 = s1; // s1 is MOVED to s2 // println!("{s1}"); // ERROR: s1 no longer valid // Clone — deep copy let s1 = String::from("hello"); let s2 = s1.clone(); // both valid // Borrowing — reference without taking ownership fn print_len(s: &String) { // immutable borrow println!("{}", s.len()); } fn add_excl(s: &mut String) { // mutable borrow s.push('!'); } // Rules: // 1. Many immutable references (&T) OR one mutable reference (&mut T) // 2. References must always be valid (no dangling) Structs struct User { name: String, age: u32, active: bool, } let user = User { name: String::from("Alex"), age: 25, active: true, }; // Access println!("{}", user.name); // Update syntax let user2 = User { age: 26, ..user }; // Tuple struct struct Point(f64, f64); let p = Point(1.0, 2.0); // Methods impl User { // Constructor (convention) fn new(name: &str, age: u32) -> Self { Self { name: name.to_string(), age, active: true } } // Method (takes &self) fn greet(&self) -> String { format!("Hi, I'm {}", self.name) } } Enums and Pattern Matching enum Direction { North, South, East, West } // Enums with data enum Shape { Circle(f64), // radius Rectangle(f64, f64), // width, height Triangle { base: f64, height: f64 }, } // Pattern matching with match match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, Shape::Triangle { base, height } => 0.5 * base * height, } // if let — match a single pattern if let Some(value) = optional { println!("Got: {value}"); } // let else — match or diverge let Some(value) = optional else { return; }; Option and Result // Option<T> — value or nothing let name: Option<&str> = Some("Alex"); let empty: Option<&str> = None; name.unwrap() // "Alex" (panics if None) name.unwrap_or("Unknown") // "Alex" (or default) name.is_some() // true name.is_none() // false name.map(|n| n.len()) // Some(4) // Result<T, E> — success or error fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("division by zero".to_string()) } else { Ok(a / b) } } // ? operator — propagate errors fn read_file(path: &str) -> Result<String, std::io::Error> { let content = std::fs::read_to_string(path)?; // returns Err early Ok(content) } // Handle Result match divide(10.0, 3.0) { Ok(result) => println!("{result}"), Err(e) => println!("Error: {e}"), } Traits // Define a trait trait Greet { fn greet(&self) -> String; // Default implementation fn hello(&self) -> String { format!("Hello from {}", self.greet()) } } // Implement for a type impl Greet for User { fn greet(&self) -> String { self.name.clone() } } // Trait as parameter fn print_greeting(item: &impl Greet) { println!("{}", item.greet()); } // Trait bound syntax fn print_greeting<T: Greet>(item: &T) { println!("{}", item.greet()); } // Common derive traits #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Point { x: i32, y: i32 } Collections // Vec — dynamic array let mut v = vec![1, 2, 3]; v.push(4); v.pop(); // Some(4) v.len(); // 3 v[0]; // 1 (panics if out of bounds) v.get(0); // Some(&1) (safe) v.contains(&2); // true v.iter().filter(|&&x| x > 1).collect::<Vec<_>>(); // HashMap use std::collections::HashMap; let mut map = HashMap::new(); map.insert("name", "Alex"); map.get("name"); // Some(&"Alex") map.contains_key("name"); // true map.entry("age").or_insert("25"); for (key, value) in &map { } // HashSet use std::collections::HashSet; let mut set = HashSet::new(); set.insert(1); set.contains(&1); // true Iterators let nums = vec![1, 2, 3, 4, 5]; nums.iter().map(|x| x * 2).collect::<Vec<_>>(); // [2, 4, 6, 8, 10] nums.iter().filter(|&&x| x > 2).collect::<Vec<_>>(); // [3, 4, 5] nums.iter().sum::<i32>(); // 15 nums.iter().any(|&x| x > 3); // true nums.iter().all(|&x| x > 0); // true nums.iter().find(|&&x| x > 3); // Some(&4) nums.iter().position(|&x| x == 3); // Some(2) nums.iter().enumerate(); // (index, &value) nums.iter().zip(other.iter()); // pair elements nums.iter().take(3).collect::<Vec<_>>(); // [1, 2, 3] nums.iter().skip(2).collect::<Vec<_>>(); // [3, 4, 5] nums.iter().flat_map(|x| vec![x, x * 10]); nums.iter().fold(0, |acc, &x| acc + x); // 15 Control Flow // if / else (is an expression) let status = if age >= 18 { "adult" } else { "minor" }; // loop (infinite, break with value) let result = loop { if condition { break 42; } }; // while while count > 0 { count -= 1; } // for for i in 0..5 { } // 0, 1, 2, 3, 4 for i in 0..=5 { } // 0, 1, 2, 3, 4, 5 for item in &vec { } // iterate by reference for item in vec { } // iterate by value (moves) Closures let add = |a: i32, b: i32| -> i32 { a + b }; let double = |x| x * 2; // types inferred let greet = || println!("Hello"); // Closures capture variables let name = String::from("Alex"); let greet = || println!("Hello {name}"); // borrows name let greet = move || println!("Hello {name}"); // takes ownership Cargo Commands Command Description cargo new my_app Create a new project cargo run Build and run cargo build Build (debug) cargo build --release Build (optimized) cargo test Run tests cargo check Fast compile check (no binary) cargo clippy Lint your code cargo fmt Auto-format code cargo add serde Add a dependency cargo doc --open Generate and open docs Lifetimes // Lifetimes tell the compiler how long references are valid fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() > b.len() { a } else { b } } // Struct with a reference needs a lifetime struct Excerpt<'a> { text: &'a str, } Rule: if a function returns a reference, it must come from one of the inputs (annotated with the same lifetime). ...

July 13, 2026 · 7 min