Build a Todo List App with Structs, Vectors & Methods - Rust by Examples #5:
Video: Build a Todo List App with Structs, Vectors & Methods - Rust by Examples #5: by Taught by Celeste AI - AI Coding Coach
Watch full page →Build a Todo List App with Structs, Vectors & Methods in Rust
In this tutorial, you will create a command-line todo list application in Rust that supports adding, listing, completing, and deleting tasks. You'll learn how to define structs to group task data, use vectors to store dynamic lists, and implement methods with impl blocks to organize functionality.
Code
use std::io::{self, Write};
struct Todo {
id: u32,
title: String,
completed: bool,
}
struct TodoList {
todos: Vec<Todo>,
next_id: u32,
}
impl TodoList {
// Constructor: create a new empty todo list
fn new() -> Self {
TodoList {
todos: Vec::new(),
next_id: 1,
}
}
// Add a new todo with a title
fn add(&mut self, title: String) {
let todo = Todo {
id: self.next_id,
title,
completed: false,
};
self.todos.push(todo);
self.next_id += 1;
println!("Task added with ID {}", self.next_id - 1);
}
// List all todos with their status
fn list(&self) {
for todo in &self.todos {
let status = if todo.completed { "[x]" } else { "[ ]" };
println!("{} {}: {}", status, todo.id, todo.title);
}
}
// Mark a todo as completed by ID
fn complete(&mut self, id: u32) {
for todo in &mut self.todos {
if todo.id == id {
todo.completed = true;
println!("Task {} marked as completed.", id);
return;
}
}
println!("Task ID {} not found.", id);
}
// Delete a todo by ID
fn delete(&mut self, id: u32) {
let original_len = self.todos.len();
self.todos.retain(|todo| todo.id != id);
if self.todos.len() < original_len {
println!("Task {} deleted.", id);
} else {
println!("Task ID {} not found.", id);
}
}
}
fn show_menu() {
println!("\nTodo List Menu:");
println!("1. Add task");
println!("2. List tasks");
println!("3. Complete task");
println!("4. Delete task");
println!("5. Quit");
print!("Enter choice: ");
io::stdout().flush().unwrap();
}
fn main() {
let mut todo_list = TodoList::new();
loop {
show_menu();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let choice = input.trim();
match choice {
"1" => {
print!("Enter task title: ");
io::stdout().flush().unwrap();
let mut title = String::new();
io::stdin().read_line(&mut title).unwrap();
todo_list.add(title.trim().to_string());
}
"2" => todo_list.list(),
"3" => {
print!("Enter task ID to complete: ");
io::stdout().flush().unwrap();
let mut id_str = String::new();
io::stdin().read_line(&mut id_str).unwrap();
if let Ok(id) = id_str.trim().parse() {
todo_list.complete(id);
} else {
println!("Invalid ID.");
}
}
"4" => {
print!("Enter task ID to delete: ");
io::stdout().flush().unwrap();
let mut id_str = String::new();
io::stdin().read_line(&mut id_str).unwrap();
if let Ok(id) = id_str.trim().parse() {
todo_list.delete(id);
} else {
println!("Invalid ID.");
}
}
"5" => {
println!("Goodbye!");
break;
}
_ => println!("Invalid choice, please try again."),
}
}
}
Key Points
- Structs group related data with named fields, such as id, title, and completed status for a todo.
- Vectors provide a growable array to store multiple todo items dynamically.
- Impl blocks define methods on structs, encapsulating functionality like adding, listing, completing, and deleting todos.
- Pattern matching with match expressions handles user input cleanly for menu choices.
- Rust’s standard input/output (std::io) allows reading from the terminal and flushing output for interactive CLI apps.