Tools: Rust Looks Scary. It's Not. You're Just Learning It Wrong.

Tools: Rust Looks Scary. It's Not. You're Just Learning It Wrong.

Source: Dev.to

What Rust Actually Does ## Setting Up ## Variables & Mutability ## Data Types ## Functions ## Control Flow ## Structs — Rust's Version of Objects ## Enums — More Powerful Than You Think ## The Big Three: Ownership, Borrowing, References ## Ownership ## Borrowing ## Mutable References ## Option — No More Null ## Result — Handling Errors Properly ## Vectors & HashMaps ## Mini Project — CLI Todo App ## The Compiler Is Not Your Enemy ## Common Mistakes to Avoid ## How to Practice ## What's Next? JavaScript runs your frontend. Node.js handles your backend. But then you hit a wall. Your app slows down. Memory keeps climbing. You Google "why is my Node server eating 2GB of RAM" and some Reddit thread just says — use Rust. So you look it up. And immediately see: I don't blame you. But here's the thing — that's advanced Rust. Nobody starts there. Let's start where you should. Rust is a systems programming language built for two things that rarely come together: speed and safety. Think operating systems, game engines, databases, web servers at scale. The kind of software where a crash isn't just a bug report — it's a disaster. C and C++ owned this space for decades. They're fast. But dangerous. Memory leaks, data races, crashes that appear months later. Rust eliminates entire categories of bugs at compile time. Without a garbage collector. Without a runtime. That's why Rust has been the most loved language on Stack Overflow for nine years straight. Not most used. Most loved. That's a different metric entirely. Create your first project: You'll see Hello, world! In most languages, variables are mutable by default. Rust flips this. This feels restrictive at first. But most bugs come from changing something you didn't mean to change. Rust makes you declare intent upfront. For values that should never change anywhere: Rule of thumb: Start with let. Add mut only when you genuinely need it. Rust is statically typed — every value has a type, and the compiler knows all of them before your code even runs. Most of the time, Rust infers types for you: The "no semicolon = return value" thing trips everyone up early. Once it clicks, it actually feels clean. Add a semicolon and the expression becomes a statement — returns nothing. &self = reading the struct. &mut self = modifying it. This pattern shows up everywhere in Rust. Rust enums can hold data. This makes them genuinely different from enums in other languages. That match block is exhaustive. Forget a case → compiler error. Not a runtime panic. Not a wrong result. A compile-time error. Before your code runs. This is what people mean when they say "Rust is hard." It's not impossible. It's just a different mental model. Give it real attention here and everything else in Rust becomes much clearer. Every value in Rust has exactly one owner. When the owner is gone, the value is dropped (memory freed). Automatically. No garbage collector. In JavaScript, both variables would point to the same string. In Rust, ownership transfers. s1 is gone. Why? If two variables could own the same memory, who frees it? Double-free = crash. Rust makes this impossible — at compile time. What if you need to use a value without taking ownership? You borrow it with &. The rules Rust enforces: This eliminates an entire class of data race bugs. At compile time. Rust has no null. Instead, there's Option<T>: The key: Rust forces you to handle None. You cannot accidentally call methods on a missing value. The compiler won't allow it. For operations that can fail: The ? operator propagates errors automatically — you don't rewrite the same match block everywhere. Let's put everything together: Run with cargo run. Fully working CLI app — structs, methods, HashMap, Option, pattern matching, and user input. Everything from this guide in one place. Rust's error messages are some of the best in any language: This error saved you from a bug. The compiler caught it before your code ever ran. Read every error. The whole thing. Fighting the borrow checker. If it's complaining, your design needs adjusting — not the rules. Cloning everything. clone() works, but it usually means you haven't understood ownership yet. Understand first, then decide. Jumping to async Rust too early. Get the basics solid first. Skimming error messages. They're verbose for a reason. Read them fully. Comparing everything to JavaScript. Rust solves different problems with a different mental model. Let it be different. Build small, real things: Use cargo check instead of cargo build while iterating — gives you errors much faster. The Rust Book is free, excellent, and written by people who care about making this approachable. Progress in Rust feels slow, and then suddenly clicks. You go from fighting the compiler to being grateful for it. That shift happens. Give it time. You now have the foundation: Variables. Functions. Structs. Enums. Ownership. Option and Result. Vectors. HashMaps. That's enough to build real things. Next in this series → Building web servers and APIs with Axum + Tokio, Rust's async runtime. Fast, clean, safe backend code. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } COMMAND_BLOCK: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } COMMAND_BLOCK: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh COMMAND_BLOCK: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh CODE_BLOCK: cargo new hello_rust cd hello_rust cargo run Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: cargo new hello_rust cd hello_rust cargo run CODE_BLOCK: cargo new hello_rust cd hello_rust cargo run CODE_BLOCK: fn main() { let name = "Luffy"; // immutable by default let mut age = 19; // mut = can be changed later // name = "Zoro"; // ❌ ERROR: cannot assign twice to immutable variable age = 20; // ✅ fine println!("Name: {}, Age: {}", name, age); } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn main() { let name = "Luffy"; // immutable by default let mut age = 19; // mut = can be changed later // name = "Zoro"; // ❌ ERROR: cannot assign twice to immutable variable age = 20; // ✅ fine println!("Name: {}, Age: {}", name, age); } CODE_BLOCK: fn main() { let name = "Luffy"; // immutable by default let mut age = 19; // mut = can be changed later // name = "Zoro"; // ❌ ERROR: cannot assign twice to immutable variable age = 20; // ✅ fine println!("Name: {}, Age: {}", name, age); } CODE_BLOCK: const MAX_PLAYERS: u32 = 100; // type annotation always required Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: const MAX_PLAYERS: u32 = 100; // type annotation always required CODE_BLOCK: const MAX_PLAYERS: u32 = 100; // type annotation always required CODE_BLOCK: fn main() { // Integers let score: i32 = 100; // signed 32-bit let health: u8 = 255; // unsigned 8-bit (0–255) // Floats let price: f64 = 29.99; // Boolean let is_active: bool = true; // Character let grade: char = 'A'; // String types (two different things in Rust) let language: &str = "Rust"; // string slice (borrowed) let greeting: String = String::from("Hi"); // owned String println!("{} {} {} {}", score, price, is_active, language); } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn main() { // Integers let score: i32 = 100; // signed 32-bit let health: u8 = 255; // unsigned 8-bit (0–255) // Floats let price: f64 = 29.99; // Boolean let is_active: bool = true; // Character let grade: char = 'A'; // String types (two different things in Rust) let language: &str = "Rust"; // string slice (borrowed) let greeting: String = String::from("Hi"); // owned String println!("{} {} {} {}", score, price, is_active, language); } CODE_BLOCK: fn main() { // Integers let score: i32 = 100; // signed 32-bit let health: u8 = 255; // unsigned 8-bit (0–255) // Floats let price: f64 = 29.99; // Boolean let is_active: bool = true; // Character let grade: char = 'A'; // String types (two different things in Rust) let language: &str = "Rust"; // string slice (borrowed) let greeting: String = String::from("Hi"); // owned String println!("{} {} {} {}", score, price, is_active, language); } CODE_BLOCK: let x = 42; // i32 let y = 3.14; // f64 let z = true; // bool Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: let x = 42; // i32 let y = 3.14; // f64 let z = true; // bool CODE_BLOCK: let x = 42; // i32 let y = 3.14; // f64 let z = true; // bool COMMAND_BLOCK: // No return value fn greet(name: &str) { println!("Hello, {}!", name); } // Returns a value — note the arrow fn add(a: i32, b: i32) -> i32 { a + b // No semicolon = this IS the return value } fn main() { greet("Zoro"); println!("5 + 3 = {}", add(5, 3)); // 8 } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // No return value fn greet(name: &str) { println!("Hello, {}!", name); } // Returns a value — note the arrow fn add(a: i32, b: i32) -> i32 { a + b // No semicolon = this IS the return value } fn main() { greet("Zoro"); println!("5 + 3 = {}", add(5, 3)); // 8 } COMMAND_BLOCK: // No return value fn greet(name: &str) { println!("Hello, {}!", name); } // Returns a value — note the arrow fn add(a: i32, b: i32) -> i32 { a + b // No semicolon = this IS the return value } fn main() { greet("Zoro"); println!("5 + 3 = {}", add(5, 3)); // 8 } CODE_BLOCK: fn main() { let score = 87; if score >= 90 { println!("Grade: A"); } else if score >= 80 { println!("Grade: B"); } else { println!("Grade: F"); } // if as an expression — this is genuinely useful let status = if score >= 60 { "Pass" } else { "Fail" }; println!("Status: {}", status); } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn main() { let score = 87; if score >= 90 { println!("Grade: A"); } else if score >= 80 { println!("Grade: B"); } else { println!("Grade: F"); } // if as an expression — this is genuinely useful let status = if score >= 60 { "Pass" } else { "Fail" }; println!("Status: {}", status); } CODE_BLOCK: fn main() { let score = 87; if score >= 90 { println!("Grade: A"); } else if score >= 80 { println!("Grade: B"); } else { println!("Grade: F"); } // if as an expression — this is genuinely useful let status = if score >= 60 { "Pass" } else { "Fail" }; println!("Status: {}", status); } CODE_BLOCK: fn main() { // loop — runs until you break let mut count = 0; loop { count += 1; if count == 5 { break; } } // while let mut i = 0; while i < 5 { println!("{}", i); i += 1; } // for — the one you'll use most for number in 1..=5 { // 1..=5 = 1 through 5 inclusive println!("{}", number); } // iterating collections let fruits = ["apple", "banana", "mango"]; for fruit in fruits { println!("{}", fruit); } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn main() { // loop — runs until you break let mut count = 0; loop { count += 1; if count == 5 { break; } } // while let mut i = 0; while i < 5 { println!("{}", i); i += 1; } // for — the one you'll use most for number in 1..=5 { // 1..=5 = 1 through 5 inclusive println!("{}", number); } // iterating collections let fruits = ["apple", "banana", "mango"]; for fruit in fruits { println!("{}", fruit); } } CODE_BLOCK: fn main() { // loop — runs until you break let mut count = 0; loop { count += 1; if count == 5 { break; } } // while let mut i = 0; while i < 5 { println!("{}", i); i += 1; } // for — the one you'll use most for number in 1..=5 { // 1..=5 = 1 through 5 inclusive println!("{}", number); } // iterating collections let fruits = ["apple", "banana", "mango"]; for fruit in fruits { println!("{}", fruit); } } COMMAND_BLOCK: struct Player { name: String, health: u32, level: u8, } impl Player { fn new(name: &str) -> Player { Player { name: String::from(name), health: 100, level: 1, } } fn take_damage(&mut self, damage: u32) { self.health = self.health.saturating_sub(damage); // never goes below 0 } fn is_alive(&self) -> bool { self.health > 0 } fn display(&self) { println!("{} | HP: {} | Level: {}", self.name, self.health, self.level); } } fn main() { let mut player = Player::new("Luffy"); player.display(); // Luffy | HP: 100 | Level: 1 player.take_damage(30); player.display(); // Luffy | HP: 70 | Level: 1 player.take_damage(200); println!("Alive? {}", player.is_alive()); // false } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: struct Player { name: String, health: u32, level: u8, } impl Player { fn new(name: &str) -> Player { Player { name: String::from(name), health: 100, level: 1, } } fn take_damage(&mut self, damage: u32) { self.health = self.health.saturating_sub(damage); // never goes below 0 } fn is_alive(&self) -> bool { self.health > 0 } fn display(&self) { println!("{} | HP: {} | Level: {}", self.name, self.health, self.level); } } fn main() { let mut player = Player::new("Luffy"); player.display(); // Luffy | HP: 100 | Level: 1 player.take_damage(30); player.display(); // Luffy | HP: 70 | Level: 1 player.take_damage(200); println!("Alive? {}", player.is_alive()); // false } COMMAND_BLOCK: struct Player { name: String, health: u32, level: u8, } impl Player { fn new(name: &str) -> Player { Player { name: String::from(name), health: 100, level: 1, } } fn take_damage(&mut self, damage: u32) { self.health = self.health.saturating_sub(damage); // never goes below 0 } fn is_alive(&self) -> bool { self.health > 0 } fn display(&self) { println!("{} | HP: {} | Level: {}", self.name, self.health, self.level); } } fn main() { let mut player = Player::new("Luffy"); player.display(); // Luffy | HP: 100 | Level: 1 player.take_damage(30); player.display(); // Luffy | HP: 70 | Level: 1 player.take_damage(200); println!("Alive? {}", player.is_alive()); // false } COMMAND_BLOCK: enum Shape { Circle(f64), // radius Rectangle(f64, f64), // width, height } fn area(shape: Shape) -> f64 { match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, } } fn main() { println!("Circle: {:.2}", area(Shape::Circle(5.0))); // 78.54 println!("Rectangle: {:.2}", area(Shape::Rectangle(4.0, 6.0))); // 24.00 } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: enum Shape { Circle(f64), // radius Rectangle(f64, f64), // width, height } fn area(shape: Shape) -> f64 { match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, } } fn main() { println!("Circle: {:.2}", area(Shape::Circle(5.0))); // 78.54 println!("Rectangle: {:.2}", area(Shape::Rectangle(4.0, 6.0))); // 24.00 } COMMAND_BLOCK: enum Shape { Circle(f64), // radius Rectangle(f64, f64), // width, height } fn area(shape: Shape) -> f64 { match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, } } fn main() { println!("Circle: {:.2}", area(Shape::Circle(5.0))); // 78.54 println!("Rectangle: {:.2}", area(Shape::Rectangle(4.0, 6.0))); // 24.00 } CODE_BLOCK: fn main() { let s1 = String::from("hello"); let s2 = s1; // ownership MOVES to s2 // println!("{}", s1); // ❌ ERROR: s1 no longer valid println!("{}", s2); // ✅ fine } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn main() { let s1 = String::from("hello"); let s2 = s1; // ownership MOVES to s2 // println!("{}", s1); // ❌ ERROR: s1 no longer valid println!("{}", s2); // ✅ fine } CODE_BLOCK: fn main() { let s1 = String::from("hello"); let s2 = s1; // ownership MOVES to s2 // println!("{}", s1); // ❌ ERROR: s1 no longer valid println!("{}", s2); // ✅ fine } CODE_BLOCK: fn print_length(s: &String) { // borrow, don't own println!("Length: {}", s.len()); } // s goes out of scope, nothing freed — we didn't own it fn main() { let s1 = String::from("hello world"); print_length(&s1); // lend s1 println!("{}", s1); // s1 still valid } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn print_length(s: &String) { // borrow, don't own println!("Length: {}", s.len()); } // s goes out of scope, nothing freed — we didn't own it fn main() { let s1 = String::from("hello world"); print_length(&s1); // lend s1 println!("{}", s1); // s1 still valid } CODE_BLOCK: fn print_length(s: &String) { // borrow, don't own println!("Length: {}", s.len()); } // s goes out of scope, nothing freed — we didn't own it fn main() { let s1 = String::from("hello world"); print_length(&s1); // lend s1 println!("{}", s1); // s1 still valid } CODE_BLOCK: fn add_exclamation(s: &mut String) { s.push_str("!!!"); } fn main() { let mut message = String::from("Hello"); add_exclamation(&mut message); println!("{}", message); // Hello!!! } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: fn add_exclamation(s: &mut String) { s.push_str("!!!"); } fn main() { let mut message = String::from("Hello"); add_exclamation(&mut message); println!("{}", message); // Hello!!! } CODE_BLOCK: fn add_exclamation(s: &mut String) { s.push_str("!!!"); } fn main() { let mut message = String::from("Hello"); add_exclamation(&mut message); println!("{}", message); // Hello!!! } COMMAND_BLOCK: fn find_player(id: u32) -> Option<String> { if id == 1 { Some(String::from("Luffy")) } else { None } } fn main() { // Pattern match match find_player(1) { Some(name) => println!("Found: {}", name), None => println!("Not found"), } // Shorthand with default let player = find_player(99).unwrap_or(String::from("Unknown")); println!("{}", player); // Unknown // if let — when you only care about Some if let Some(name) = find_player(1) { println!("Welcome, {}!", name); } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: fn find_player(id: u32) -> Option<String> { if id == 1 { Some(String::from("Luffy")) } else { None } } fn main() { // Pattern match match find_player(1) { Some(name) => println!("Found: {}", name), None => println!("Not found"), } // Shorthand with default let player = find_player(99).unwrap_or(String::from("Unknown")); println!("{}", player); // Unknown // if let — when you only care about Some if let Some(name) = find_player(1) { println!("Welcome, {}!", name); } } COMMAND_BLOCK: fn find_player(id: u32) -> Option<String> { if id == 1 { Some(String::from("Luffy")) } else { None } } fn main() { // Pattern match match find_player(1) { Some(name) => println!("Found: {}", name), None => println!("Not found"), } // Shorthand with default let player = find_player(99).unwrap_or(String::from("Unknown")); println!("{}", player); // Unknown // if let — when you only care about Some if let Some(name) = find_player(1) { println!("Welcome, {}!", name); } } COMMAND_BLOCK: fn parse_age(input: &str) -> Result<u32, String> { match input.trim().parse::<u32>() { Ok(n) => Ok(n), Err(_) => Err(format!("'{}' is not a valid age", input)), } } fn main() { match parse_age("25") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), } match parse_age("abc") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), // this one runs } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: fn parse_age(input: &str) -> Result<u32, String> { match input.trim().parse::<u32>() { Ok(n) => Ok(n), Err(_) => Err(format!("'{}' is not a valid age", input)), } } fn main() { match parse_age("25") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), } match parse_age("abc") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), // this one runs } } COMMAND_BLOCK: fn parse_age(input: &str) -> Result<u32, String> { match input.trim().parse::<u32>() { Ok(n) => Ok(n), Err(_) => Err(format!("'{}' is not a valid age", input)), } } fn main() { match parse_age("25") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), } match parse_age("abc") { Ok(age) => println!("Valid age: {}", age), Err(e) => println!("Error: {}", e), // this one runs } } COMMAND_BLOCK: fn main() { let mut numbers = vec![1, 2, 3, 4, 5]; numbers.push(6); // Safe access — no crash on out-of-bounds println!("{:?}", numbers.get(10)); // None // Just like JS array methods let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect(); println!("{:?}", doubled); // [2, 4, 6, 8, 10, 12] println!("{:?}", evens); // [2, 4, 6] } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: fn main() { let mut numbers = vec![1, 2, 3, 4, 5]; numbers.push(6); // Safe access — no crash on out-of-bounds println!("{:?}", numbers.get(10)); // None // Just like JS array methods let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect(); println!("{:?}", doubled); // [2, 4, 6, 8, 10, 12] println!("{:?}", evens); // [2, 4, 6] } COMMAND_BLOCK: fn main() { let mut numbers = vec![1, 2, 3, 4, 5]; numbers.push(6); // Safe access — no crash on out-of-bounds println!("{:?}", numbers.get(10)); // None // Just like JS array methods let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); let evens: Vec<&i32> = numbers.iter().filter(|&&x| x % 2 == 0).collect(); println!("{:?}", doubled); // [2, 4, 6, 8, 10, 12] println!("{:?}", evens); // [2, 4, 6] } COMMAND_BLOCK: use std::collections::HashMap; fn main() { let mut scores: HashMap<String, u32> = HashMap::new(); scores.insert(String::from("Luffy"), 95); scores.insert(String::from("Zoro"), 88); // Insert only if key doesn't exist scores.entry(String::from("Nami")).or_insert(91); if let Some(s) = scores.get("Luffy") { println!("Luffy: {}", s); // 95 } for (name, score) in &scores { println!("{}: {}", name, score); } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: use std::collections::HashMap; fn main() { let mut scores: HashMap<String, u32> = HashMap::new(); scores.insert(String::from("Luffy"), 95); scores.insert(String::from("Zoro"), 88); // Insert only if key doesn't exist scores.entry(String::from("Nami")).or_insert(91); if let Some(s) = scores.get("Luffy") { println!("Luffy: {}", s); // 95 } for (name, score) in &scores { println!("{}: {}", name, score); } } COMMAND_BLOCK: use std::collections::HashMap; fn main() { let mut scores: HashMap<String, u32> = HashMap::new(); scores.insert(String::from("Luffy"), 95); scores.insert(String::from("Zoro"), 88); // Insert only if key doesn't exist scores.entry(String::from("Nami")).or_insert(91); if let Some(s) = scores.get("Luffy") { println!("Luffy: {}", s); // 95 } for (name, score) in &scores { println!("{}: {}", name, score); } } COMMAND_BLOCK: use std::collections::HashMap; use std::io; struct TodoApp { tasks: HashMap<u32, (String, bool)>, next_id: u32, } impl TodoApp { fn new() -> TodoApp { TodoApp { tasks: HashMap::new(), next_id: 1 } } fn add(&mut self, desc: &str) { self.tasks.insert(self.next_id, (desc.to_string(), false)); println!("✅ Task #{} added: {}", self.next_id, desc); self.next_id += 1; } fn complete(&mut self, id: u32) { match self.tasks.get_mut(&id) { Some(task) => { task.1 = true; println!("🎉 Task #{} done!", id); } None => println!("❌ Task #{} not found.", id), } } fn list(&self) { if self.tasks.is_empty() { println!("No tasks yet."); return; } let mut ids: Vec<&u32> = self.tasks.keys().collect(); ids.sort(); println!("\n--- Tasks ---"); for id in ids { let (desc, done) = &self.tasks[id]; let icon = if *done { "✅" } else { "⏳" }; println!("{} #{}: {}", icon, id, desc); } println!("-------------\n"); } } fn main() { let mut app = TodoApp::new(); println!("=== Rust Todo ==="); println!("Commands: add <task> | done <id> | list | quit\n"); loop { let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let input = input.trim(); if input.starts_with("add ") { app.add(&input[4..]); } else if input.starts_with("done ") { match input[5..].parse::<u32>() { Ok(id) => app.complete(id), Err(_) => println!("Enter a valid number"), } } else if input == "list" { app.list(); } else if input == "quit" { println!("Goodbye! 🦀"); break; } else { println!("Unknown command."); } } } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: use std::collections::HashMap; use std::io; struct TodoApp { tasks: HashMap<u32, (String, bool)>, next_id: u32, } impl TodoApp { fn new() -> TodoApp { TodoApp { tasks: HashMap::new(), next_id: 1 } } fn add(&mut self, desc: &str) { self.tasks.insert(self.next_id, (desc.to_string(), false)); println!("✅ Task #{} added: {}", self.next_id, desc); self.next_id += 1; } fn complete(&mut self, id: u32) { match self.tasks.get_mut(&id) { Some(task) => { task.1 = true; println!("🎉 Task #{} done!", id); } None => println!("❌ Task #{} not found.", id), } } fn list(&self) { if self.tasks.is_empty() { println!("No tasks yet."); return; } let mut ids: Vec<&u32> = self.tasks.keys().collect(); ids.sort(); println!("\n--- Tasks ---"); for id in ids { let (desc, done) = &self.tasks[id]; let icon = if *done { "✅" } else { "⏳" }; println!("{} #{}: {}", icon, id, desc); } println!("-------------\n"); } } fn main() { let mut app = TodoApp::new(); println!("=== Rust Todo ==="); println!("Commands: add <task> | done <id> | list | quit\n"); loop { let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let input = input.trim(); if input.starts_with("add ") { app.add(&input[4..]); } else if input.starts_with("done ") { match input[5..].parse::<u32>() { Ok(id) => app.complete(id), Err(_) => println!("Enter a valid number"), } } else if input == "list" { app.list(); } else if input == "quit" { println!("Goodbye! 🦀"); break; } else { println!("Unknown command."); } } } COMMAND_BLOCK: use std::collections::HashMap; use std::io; struct TodoApp { tasks: HashMap<u32, (String, bool)>, next_id: u32, } impl TodoApp { fn new() -> TodoApp { TodoApp { tasks: HashMap::new(), next_id: 1 } } fn add(&mut self, desc: &str) { self.tasks.insert(self.next_id, (desc.to_string(), false)); println!("✅ Task #{} added: {}", self.next_id, desc); self.next_id += 1; } fn complete(&mut self, id: u32) { match self.tasks.get_mut(&id) { Some(task) => { task.1 = true; println!("🎉 Task #{} done!", id); } None => println!("❌ Task #{} not found.", id), } } fn list(&self) { if self.tasks.is_empty() { println!("No tasks yet."); return; } let mut ids: Vec<&u32> = self.tasks.keys().collect(); ids.sort(); println!("\n--- Tasks ---"); for id in ids { let (desc, done) = &self.tasks[id]; let icon = if *done { "✅" } else { "⏳" }; println!("{} #{}: {}", icon, id, desc); } println!("-------------\n"); } } fn main() { let mut app = TodoApp::new(); println!("=== Rust Todo ==="); println!("Commands: add <task> | done <id> | list | quit\n"); loop { let mut input = String::new(); io::stdin().read_line(&mut input).unwrap(); let input = input.trim(); if input.starts_with("add ") { app.add(&input[4..]); } else if input.starts_with("done ") { match input[5..].parse::<u32>() { Ok(id) => app.complete(id), Err(_) => println!("Enter a valid number"), } } else if input == "list" { app.list(); } else if input == "quit" { println!("Goodbye! 🦀"); break; } else { println!("Unknown command."); } } } COMMAND_BLOCK: error[E0382]: borrow of moved value: `s1` --> src/main.rs:5:20 | 3 | let s1 = String::from("hello"); | -- move occurs because `s1` has type `String` 4 | let s2 = s1; | -- value moved here 5 | println!("{}", s1); | ^^ value borrowed here after move Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: error[E0382]: borrow of moved value: `s1` --> src/main.rs:5:20 | 3 | let s1 = String::from("hello"); | -- move occurs because `s1` has type `String` 4 | let s2 = s1; | -- value moved here 5 | println!("{}", s1); | ^^ value borrowed here after move COMMAND_BLOCK: error[E0382]: borrow of moved value: `s1` --> src/main.rs:5:20 | 3 | let s1 = String::from("hello"); | -- move occurs because `s1` has type `String` 4 | let s2 = s1; | -- value moved here 5 | println!("{}", s1); | ^^ value borrowed here after move - rustup — version manager - rustc — the compiler - cargo — build tool + package manager (think npm, but for Rust) - Unlimited read-only references (&T) — all at once ✅ - Exactly ONE mutable reference (&mut T) — no other references simultaneously ✅ - Both at the same time — ❌ never - Number guessing game (stdin, random, loops) - Temperature converter (functions, structs) - Word counter for a text file (file I/O, HashMap) - Student grade tracker (Vec, structs, sorting) - Simple calculator (enums, pattern matching)