Todo List
Model tasks with structs and booleans, print state conditionally, and count completed tasks.
Core concepts
struct, bool, conditional printing, mutable counter
How the learner should approach this project
Do not paste the complete program immediately. Create the file, type the first step, run vpp check, and then run the program when a complete entry point exists. Read the explanation before looking at the snippet. After each step, predict what the new code should do. This turns the page into a lesson rather than a code dump.
Step 1: Define Todo
Todo stores the task title and whether the task is complete. This is a small example of using a struct to keep data and its state together.
After typing the snippet, identify the new names introduced by the step. Ask what each name represents, what type the compiler should infer or check, and what value should exist after the code runs.
struct Todo {
title: string
done: bool
}Step 2: Print one task
The printer chooses a visual prefix based on done. The string concatenation joins the prefix and task title.
Before moving forward, explain how this step connects to the previous one. In particular, identify which values cross a function boundary, which values change, and which values are guaranteed by the type system.
fn print_todo(t: Todo) -> int {
if t.done {
print("[x] " + t.title)
} else {
print("[ ] " + t.title)
}
return 0
}Step 3: Create tasks and count completion
main creates three Todo values. The counter is mutable because each completed task increments it. The same data can be passed to print_todo for consistent output.
Before moving forward, explain how this step connects to the previous one. In particular, identify which values cross a function boundary, which values change, and which values are guaranteed by the type system.
fn main() -> int {
let t1 = Todo { title: "learn v++", done: true }
let t2 = Todo { title: "build a project", done: false }
let t3 = Todo { title: "ship v1.0", done: false }
print_todo(t1)
print_todo(t2)
print_todo(t3)
let mut done_count = 0
if t1.done { done_count = done_count + 1 }
if t2.done { done_count = done_count + 1 }
if t3.done { done_count = done_count + 1 }
print(done_count)
return 0
}Complete source
struct Todo {
title: string
done: bool
}
fn print_todo(t: Todo) -> int {
if t.done {
print("[x] " + t.title)
} else {
print("[ ] " + t.title)
}
return 0
}
fn main() -> int {
let t1 = Todo { title: "learn v++", done: true }
let t2 = Todo { title: "build a project", done: false }
let t3 = Todo { title: "ship v1.0", done: false }
print_todo(t1)
print_todo(t2)
print_todo(t3)
let mut done_count = 0
if t1.done { done_count = done_count + 1 }
if t2.done { done_count = done_count + 1 }
if t3.done { done_count = done_count + 1 }
print(done_count)
return 0
}Expected behavior
The complete program should produce the following output when run with the command shown below. Exact formatting should follow the current V++ runtime.
[x] learn v++ [ ] build a project [ ] ship v1.0 1
Run the project
vpp run projects/18-todo-list/main.vppWhat the learner should understand after this project
Structs can represent real application records. A bool field can drive display logic while a mutable counter summarizes state.
Common mistakes to teach
- Using the wrong field type
- Forgetting that done is a bool
- Trying to increment an immutable counter
Practice extension
Add a fourth task. Then change done_count to use a loop over an array if your current compiler supports arrays of structs in your project.
A strong learner should be able to explain the program without looking at the code, rebuild the core idea from memory, and make the practice change without copying a solution.
Full program
struct Todo {
title: string
done: bool
}
fn print_todo(t: Todo) -> int {
if t.done {
print("[x] " + t.title)
} else {
print("[ ] " + t.title)
}
return 0
}
fn main() -> int {
let t1 = Todo { title: "learn v++", done: true }
let t2 = Todo { title: "build a project", done: false }
let t3 = Todo { title: "ship v1.0", done: false }
print_todo(t1)
print_todo(t2)
print_todo(t3)
let mut done_count = 0
if t1.done { done_count = done_count + 1 }
if t2.done { done_count = done_count + 1 }
if t3.done { done_count = done_count + 1 }
print(done_count)
return 0
}