Chapter 23: Unfinished projects: Projects for you to finish
The chapter introduces runnable but deliberately minimal projects for readers to develop further and explains why they need to be run locally rather than in the Playground.
After this chapter you will be able to
- Working but unfinished projects. Explain why the projects are called unfinished even though they run, and what the reader is expected to do with them.
- Typing tutor user experience. Describe the typing tutor’s intended flow: it displays text from a file for the user to type, shows typing errors, and reports performance after the test.
- Crossterm keyboard input. Explain why the typing tutor uses Crossterm: it can detect and react to user input as it happens, though this project needs only keyboard input.
- Key press and release events. Describe what the example shows when Crossterm's read() function runs in a loop: the output includes key presses and releases, and Shift affects the reported characters.
- Key press event handling. Explain how the typing tutor selects useful keyboard events and which KeyEvent fields matter for updating the user's input.
- Typing text and display. Describe how the tutor gets the text to copy and displays it alongside the user's input, including the problem with the resulting output.
- Character comparison and feedback. Describe how zipping the target text with the user’s input supports visible mistake markers and a final correct-letter count.
- Typing input length limit. Explain what happens when the user keeps typing past the end of the test text and why user_input should be limited to the length of file_content.
- Wikipedia summary API access. Explain that Wikipedia offers an API endpoint returning an article summary and other information without requiring registration or an API key, and describe how to view a sample response in a browser.
- Initial Wikipedia search behavior. Describe how the initial App uses user_input and what happens when the user presses Enter with a nonsense word or a real search term.
- Relevant JSON fields and deserialization. Explain why the program keeps only title, description, and extract from the JSON response, and describe how it converts that JSON into a struct.
- Error handling during refactoring. Explain how replacing `.unwrap()` with `?` changes error handling in the searcher, and why both `App::get_article()` and `main()` return `Result<(), Box<dyn Error>>`.
- Searcher improvement opportunities. Describe the searcher's current feedback gap and the two improvements proposed here: helping users when a page is missing or a connection fails, and offering a choice of Wikipedia languages.
- Appeal of terminal UI crates. Explain why the project uses crates for a terminal-based interface rather than relying on Crossterm alone.
- Terminal drawing loop. Describe how ratatui's Terminal, backend, and draw closure fit together to display an interface.
- Two-part screen layout. Describe how the app divides the available terminal screen into two horizontal parts.
- Widgets and rendering areas. Explain how text and borders become visible in the laid-out terminal interface.
- Stopwatch states and transitions. Describe the stopwatch’s three states, what each displays, and how key presses move between them.
- Elapsed time display calculation. Trace how the stopwatch measures elapsed milliseconds and separates them into minutes, seconds, and hundredths for display.
- Nonblocking key event checks. Explain why the terminal loop uses crossterm’s poll() instead of read() and what a zero Duration accomplishes.
- Main function error handling. Explain how the cleanup replaces calls to .unwrap() with the question mark operator and changes main() to return Result<(), anyhow::Error>.
- Helper functions and readability. Describe the two pieces of code moved into helpers and explain the section’s rule of thumb for when helpers improve or hurt readability.
- Focus-aware screen redrawing. Explain why the app might avoid redrawing while its window is not visible and identify the crossterm events suggested for detecting that situation.
- Stopped-state string allocations. Explain why repeatedly returning a String may be wasteful when the stopwatch is NotStarted or Done.
What it builds on
- Crates and Cargo.toml (chapter 16) · The earlier introduction to crates and Cargo.toml.
- Opening existing files (chapter 18) · Reading an existing file into a String.
- Cycling, zipping, folding, and more (chapter 9) · The earlier introduction to zipping iterators.
- The ? operator (chapter 6) · The question mark operator used in place of some unwrap calls.
- Structs (chapter 4) · Structs as a way to hold related data.
- The reqwest crate (chapter 19) · Prior use of the reqwest crate.
- serde (chapter 17) · The earlier introduction to serde and deserialization.
- Using a Box to handle multiple error types (chapter 13) · using `Box<dyn Error>` to handle errors
Section by section
Introduction
The chapter introduces runnable but deliberately minimal projects for readers to develop further and explains why they need to be run locally rather than in the Playground.
- Working but unfinished projects: Explain why the projects are called unfinished even though they run, and what the reader is expected to do with them.
Why it matters: You can start with a working program rather than build one from scratch. - Local system access requirements: Explain why the new CLI and GUI crates used in these projects must be tried on a computer rather than in the Playground.
Why it matters: Running the projects locally gives their interfaces the system access they need.
Key terms: unfinished projects, cargo run, basic functionality, crates, command-line interfaces (CLIs), graphical user interfaces (GUIs), Playground, system resources
23.1 Setup for the last two chapters
The section explains why the six unfinished projects are presented in four stages rather than showing the full code after each addition.
23.2 Typing tutor
Introduces the typing tutor project and describes what the finished app is meant to do.
- Typing tutor user experience: Describe the typing tutor’s intended flow: it displays text from a file for the user to type, shows typing errors, and reports performance after the test.
Why it matters: This gives you the intended behavior to keep in mind as you build the project.
Key terms: typing tutor, text file, typing test
23.2.1 Setup and first code
Introduces Crossterm for the typing tutor and shows what its keyboard input events look like when read in a loop.
- Crossterm keyboard input: Explain why the typing tutor uses Crossterm: it can detect and react to user input as it happens, though this project needs only keyboard input.
Why it matters: It explains why Crossterm is useful for an interactive typing tutor. - Key press and release events: Describe what the example shows when Crossterm's read() function runs in a loop: the output includes key presses and releases, and Shift affects the reported characters.
Why it matters: A typing tutor must interpret the events it receives, not assume each typed character produces only one event.
Key terms: Crossterm, keyboard input, read(), pressing keys, releasing keys, shift
23.2.2 Developing the code
This section selects key-press events for a typing tutor, loads text for the user to copy, and shows why the current output needs cleanup.
- Key press event handling: Explain how the typing tutor selects useful keyboard events and which KeyEvent fields matter for updating the user's input.
Why it matters: Filtering events keeps the tutor from changing the typed text for irrelevant events or key releases. - Typing text and display: Describe how the tutor gets the text to copy and displays it alongside the user's input, including the problem with the resulting output.
Why it matters: Keeping the target and typed text separate makes them visible for comparison, while exposing a display problem to fix.
Key terms: Event, Key, if let, KeyEvent, code, kind, typing.txt, read_to_string(), String, user_input
23.2.3 Further development and cleanup
This section explains how to clear and update the typing tutor’s display, mark typing mistakes, count correct characters, and tidy the code.
- Screen clearing command: Explain how the typing tutor clears the whole screen after each keypress using Crossterm’s execute! macro and Clear command.
Why it matters: Clearing the screen lets the app present an updated display as the user types. - Character comparison and feedback: Describe how zipping the target text with the user’s input supports visible mistake markers and a final correct-letter count.
Why it matters: The same character-by-character comparison provides both immediate feedback and the final accuracy count. - Typing app code cleanup: Describe the two proposed changes that make the typing tutor’s main function cleaner.
Why it matters: These changes organize the app without changing its central typing-comparison idea.
Key terms: Crossterm, execute!, Clear, ClearType::All, .zip(), iterator, content to type, user’s output, .unwrap(), question mark operator
23.2.4 Over to you
This section offers ways to extend the typing tutor and explains why input should stop growing at the end of the test text.
- Typing input length limit: Explain what happens when the user keeps typing past the end of the test text and why user_input should be limited to the length of file_content.
Why it matters: An input-length limit would prevent invisible keystrokes from making backspace seem unresponsive.
Key terms: user_input, file_content, backspace
23.3 Wikipedia article summary searcher
Introduces a Wikipedia summary-search project and explains why its API is easy to try.
- Wikipedia summary API access: Explain that Wikipedia offers an API endpoint returning an article summary and other information without requiring registration or an API key, and describe how to view a sample response in a browser.
Why it matters: This explains why the summary-search project can get started without API credentials.
Key terms: Wikipedia API, endpoint, summary, PAGE_NAME_HERE
23.3.1 Setup and first code
Sets up the Wikipedia summary searcher and shows that its first version receives Wikipedia responses but still needs to make them readable.
- Initial Wikipedia search behavior: Describe how the initial App uses user_input and what happens when the user presses Enter with a nonsense word or a real search term.
Why it matters: It identifies what the first version already does and why the response needs further work. - HTTP crate tradeoff: Explain which dependencies the project uses and why ureq is mentioned as an alternative to reqwest.
Why it matters: It explains the practical dependency choice before working on the searcher.
Key terms: App, user_input, Enter, JSON response, crossterm, reqwest, ureq, compiling time
23.3.2 Developing the code
The section selects useful fields from a Wikipedia JSON response and converts them into a readable Rust struct.
- Relevant JSON fields and deserialization: Explain why the program keeps only title, description, and extract from the JSON response, and describe how it converts that JSON into a struct.
Why it matters: Keeping only the useful fields makes the article information easier to work with and read.
Key terms: title, description, extract, Deserialize, serde, serde_json
23.3.3 Further development and cleanup
This subsection refactors the Wikipedia article searcher to handle errors with `?` and make its output cleaner.
- Error handling during refactoring: Explain how replacing `.unwrap()` with `?` changes error handling in the searcher, and why both `App::get_article()` and `main()` return `Result<(), Box<dyn Error>>`.
Why it matters: It explains how the refactored searcher handles errors without calls to `.unwrap()`. - Cleaner App output: Describe the change to `App` that improves the searcher’s output.
Why it matters: It connects the `Display` implementation to the visible output cleanup.
Key terms: .unwrap(), question mark operator, Box<dyn Error>, Result<(), Box<dyn Error>>, get_article(), Display, App struct
23.3.4 Over to you
Identifies ways to improve the working Wikipedia summary searcher, especially its error feedback and language options.
- Searcher improvement opportunities: Describe the searcher's current feedback gap and the two improvements proposed here: helping users when a page is missing or a connection fails, and offering a choice of Wikipedia languages.
Why it matters: These are useful next steps for making the working searcher more helpful and flexible.
Key terms: error info, broken internet connection, switch languages
23.4 Terminal stopwatch and clock
Introduces a terminal-based stopwatch and clock project and explains the appeal of crates for building terminal interfaces.
- Appeal of terminal UI crates: Explain why the project uses crates for a terminal-based interface rather than relying on Crossterm alone.
Why it matters: It explains why a terminal interface is an appealing choice for the stopwatch and clock project.
Key terms: Crossterm, terminal-based app, TUI, cargo run
23.4.1 Setup and first code
The section sets up a ratatui terminal interface by explaining its drawing loop, a two-part layout, and the widgets rendered into it.
- Ratatui crate choice: Explain why the project uses ratatui rather than the original tui crate and how crossterm fits in.
Why it matters: This explains the project's crate choice without treating crossterm as a competing interface library. - Terminal drawing loop: Describe how ratatui's Terminal, backend, and draw closure fit together to display an interface.
Why it matters: It gives the reader the basic structure of the interface's drawing code. - Two-part screen layout: Describe how the app divides the available terminal screen into two horizontal parts.
Why it matters: The layout determines where the interface's content can be drawn, including when the terminal is resized. - Widgets and rendering areas: Explain how text and borders become visible in the laid-out terminal interface.
Why it matters: It distinguishes allocating screen space from putting visible content into that space.
Key terms: ratatui, tui, crossterm, backend, Terminal, .draw(), closure, .render_widget(), Layout, direction
23.4.2 Developing the code
This section builds a readable clock and a three-state stopwatch, then keeps the terminal display updating without waiting for a key press.
- Readable clock display: Explain how the code gets the current UTC datetime and makes it readable with chrono’s DateTime.format() method.
Why it matters: Formatting turns the clock’s datetime into a display people can readily read. - Stopwatch states and transitions: Describe the stopwatch’s three states, what each displays, and how key presses move between them.
Why it matters: The states determine what the stopwatch displays and what each key press does. - Elapsed time display calculation: Trace how the stopwatch measures elapsed milliseconds and separates them into minutes, seconds, and hundredths for display.
Why it matters: This calculation turns one elapsed-time value into the stopwatch’s displayed time. - Nonblocking key event checks: Explain why the terminal loop uses crossterm’s poll() instead of read() and what a zero Duration accomplishes.
Why it matters: Checking without waiting allows the time display to keep changing when the user does nothing.
Key terms: chrono, DateTime, .format(), UTC datetime, enum, Not started, Running, Stopped, Instant, Instant::now()
23.4.3 Further development and cleanup
This section cleans up the terminal stopwatch and clock by changing error handling and extracting helper functions, while weighing the readability costs of refactoring.
- Main function error handling: Explain how the cleanup replaces calls to .unwrap() with the question mark operator and changes main() to return Result<(), anyhow::Error>.
Why it matters: It shows the specific error-handling cleanup made to this project. - Helper functions and readability: Describe the two pieces of code moved into helpers and explain the section’s rule of thumb for when helpers improve or hurt readability.
Why it matters: The point of extracting code is to make main() easier to read without hiding what the program does.
Key terms: .unwrap(), question mark operator, anyhow, Result<(), anyhow::Error>, helper function, Block, formatted UTC time, readability cleanup
23.4.4 Over to you
Suggests ways to improve the terminal stopwatch and clock’s display, add features, and avoid unnecessary work.
- Stopwatch display consistency: Describe why the stopwatch display could look uneven and suggest a cleaner presentation, such as making changing time values occupy a consistent width.
Why it matters: A consistent display is easier to read as the stopwatch changes. - Focus-aware screen redrawing: Explain why the app might avoid redrawing while its window is not visible and identify the crossterm events suggested for detecting that situation.
Why it matters: Avoiding redraws that nobody can see could reduce wasted work. - Stopped-state string allocations: Explain why repeatedly returning a String may be wasteful when the stopwatch is NotStarted or Done.
Why it matters: Repeatedly allocating a String for an unchanged time may be unnecessary work.
Key terms: stopwatch, display, Event, crossterm, FocusGained, FocusLost, String, NotStarted, Done, allocate memory
23.5 Summary
Concepts explained in this chapter
- CrosstermCrossterm is a Rust cross-platform terminal I/O library for handling events such as keyboard input, mouse input, and terminal resizing.
- terminal user interfaceA terminal user interface is an interactive text-based interface rendered in a terminal rather than a graphical window.
- pollpoll is the operation by which an asynchronous runtime advances a future toward completion and obtains its current state or output.
- question mark operatorThe question mark operator is Rust syntax that propagates an error from a Result or absence from an Option.
- if letif let is Rust conditional pattern-matching syntax that executes a block when a value matches a specified pattern and otherwise continues without executing that block.
- keyA key is a value used to identify and look up an associated value in a map collection.
- read_to_stringread_to_string is a filesystem operation that reads a file's contents into a String during program execution.
- functionA function is a named unit of executable code that can accept inputs and produce a result.
- DisplayDisplay printing formats a Rust value using its user-facing Display formatting behavior.
- structA struct is a composite data type or structure that groups related fields or values into a single unit.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
