Code

Refactor main.go — a clean display and a real check

The menu loop stays exactly as it was — we add two functions and one check. Everything else in your main.go is untouched.

1. A correct title check — validateTitle. It counts characters (runes), not bytes:

func validateTitle(title string) string {
	title = strings.TrimSpace(title)
	n := utf8.RuneCountInString(title)
	if n == 0 {
		return "Title cannot be empty."
	}
	if n > 60 {
		return fmt.Sprintf("Title is too long: %d characters (max 60).", n)
	}
	return ""
}

Had we written len(title) > 60, a Lithuanian title with diacritics would be rejected too early — len counts bytes. This one function is step 2's gotcha, applied. (Add "unicode/utf8" to the imports.)

2. Use it in "Set title". One check added inside case "2" — validate before storing:

		case "2":
			fmt.Print("New title: ")
			title := readLine()
			if msg := validateTitle(title); msg != "" {
				fmt.Println(msg)
				continue
			}
			it.Title = title
			fmt.Println("Title set to:", title)

3. A clean, ALIGNED display — formatItem. %-30s left-aligns the title in a 30-column field, %3d/%-3d pads the numbers, so rows line up:

func formatItem(it Item) string {
	status := "reading"
	if it.Read {
		status = "done"
	}
	return fmt.Sprintf("%-30s %3d/%-3d  [%s]", it.Title, it.PagesRead, it.Pages, status)
}

printItem now just calls it — fmt.Println(formatItem(it)). In lesson 8, with many records, the alignment pays off: a tidy table for free.

Verify — set a multi-word Lithuanian title, then show it:

> 2
New title: Ąžuolas ir uosis
Title set to: Ąžuolas ir uosis

> 1
Ąžuolas ir uosis                 0/380  [reading]

The title is read in full (spaces included — readLine from lesson 5), and the columns line up.

Gotcha (fmt.Scanln and spaces). If you read the title with fmt.Scanln(&title), "Ąžuolas ir uosis" becomes just "Ąžuolas" — the rest of the line stays in the buffer and scrambles the next read. For multi-word text, always read the whole line with readLine, not Scanln.