Let's think about this for a second
Part 1 used a function-based approach, but in Part 2 we'll refactor the code to be more solid using the receiver method pattern you learned in the Methods chapter. We'll turn the tasks slice into a field of a TaskManager struct, and rewrite Add, List, Complete, and Remove as methods on that struct. Since Complete and Remove need to handle what happens when an ID is wrong, we'll put the error return pattern from the Errors chapter into practice. By the end of this stage, the project will follow a more proper Go design, combining struct, methods, and error handling.
Let's build it for real
Define a TaskManager struct with a single tasks []Task field. Using a pointer receiver (m *TaskManager), write four methods: Add(title string), List(), Complete(id int) error, and Remove(id int) error. In both Complete and Remove, use a for loop to find the ID, and if it's not found, return fmt.Errorf("task %d not found", id). In Remove, use the slice trick (append(tasks[:i], tasks[i+1:]...)) to drop the task from the list. In main(), call Complete/Remove, check the error with if err != nil, and print it.
Code Example
package main
import "fmt"
type Task struct {
ID int
Title string
Done bool
}
type TaskManager struct {
tasks []Task
}
func (m *TaskManager) Add(title string) {
t := Task{ID: len(m.tasks) + 1, Title: title}
m.tasks = append(m.tasks, t)
}
func (m *TaskManager) List() {
for _, t := range m.tasks {
status := "[ ]"
if t.Done {
status = "[x]"
}
fmt.Printf("%s %d. %s\n", status, t.ID, t.Title)
}
}
func (m *TaskManager) Complete(id int) error {
for i := range m.tasks {
if m.tasks[i].ID == id {
m.tasks[i].Done = true
return nil
}
}
return fmt.Errorf("task %d not found", id)
}
func (m *TaskManager) Remove(id int) error {
for i, t := range m.tasks {
if t.ID == id {
m.tasks = append(m.tasks[:i], m.tasks[i+1:]...)
return nil
}
}
return fmt.Errorf("task %d not found", id)
}
func main() {
manager := &TaskManager{}
manager.Add("Learn Go basics")
manager.Add("Build task manager")
if err := manager.Complete(1); err != nil {
fmt.Println("Error:", err)
}
if err := manager.Remove(5); err != nil {
fmt.Println("Error:", err)
}
manager.List()
}After completing task 1, trying to remove ID 5 will print "Error: task 5 not found".Try it in 5 minutes
Within 5 minutes, add a new method, Update(id int, newTitle string) error, and implement it yourself so it can find a task by ID and change its title.
One quick word of caution
Keep in mind that append(m.tasks[:i], m.tasks[i+1:]...) inside the Remove method shares the slice's underlying array, so a data race is possible if there's concurrent access.