Todo List

Model tasks with structs and booleans, print state conditionally, and count completed tasks.

1

Core concepts

struct, bool, conditional printing, mutable counter

2

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.

3

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.

main.vpp
struct Todo {
    title: string
    done: bool
}
4

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.

main.vpp
fn print_todo(t: Todo) -> int {
    if t.done {
        print("[x] " + t.title)
    } else {
        print("[ ] " + t.title)
    }
    return 0
}
5

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.

main.vpp
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
}
6

Complete source

main.vpp
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
}
7

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
8

Run the project

terminal
vpp run projects/18-todo-list/main.vpp
9

What 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.

10

Common mistakes to teach

  1. Using the wrong field type
  2. Forgetting that done is a bool
  3. Trying to increment an immutable counter
11

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.

12

Full program

main.vpp
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
}
vpp
$ ready. Click Test program.