Rust: from zero to your own organizer Lesson 36 of 60
A command as a checked intention
Lesson 36. Separate parsing launch words from acting on the organizer and explain each error.
This text was translated with AI.
Prerequisites: lessons 29–35. If Option and Result are unclear, revisit lesson 31.
Familiar image and recall map
At a service desk, someone checks the request form before carrying it out. The organizer can do the same: turn words into a checked command first. Command parsing checks the action name and the data it requires. It does not change the task list yet. The analogy has a limit: this program does not understand arbitrary sentences; it accepts only the forms we define.
Words → shape check → Result<Command, String> → Ok with a command or Err with a reason → action later. Command is our own enum from lesson 29. Add(String) carries a title; List carries no data. Option<&str> means a borrowed piece of text may be present (Some) or absent (None), as in lesson 31. A tuple of three values lets match check the action, title and extra argument together. title.trim().is_empty() tests whether anything remains after removing edge spaces. String::from(title) makes an owned string: the original arguments may disappear while the command still needs its title. _ covers the remaining shapes and returns an explanation. Here Ok(...) and Err(...) are result values; neither prints.
Parse, then show
Save the previous src/main.rs in your organizer project, replace it completely, and run cargo run beside Cargo.toml. This teaching example parses fixed words; we will connect actual arguments from lesson 35 when assembling the full organizer. No other files or dependencies change. Predict the output before running.
enum Command {
Add(String),
List,
}
fn parse(action: &str, title: Option<&str>, extra: Option<&str>) -> Result<Command, String> {
match (action, title, extra) {
("list", None, None) => Ok(Command::List),
("add", Some(title), None) => {
if title.trim().is_empty() {
Err(String::from("Empty title"))
} else {
Ok(Command::Add(String::from(title)))
}
}
_ => Err(String::from("Unknown command or argument count")),
}
}
fn show(result: Result<Command, String>) {
match result {
Ok(Command::Add(title)) => println!("Add: {title}"),
Ok(Command::List) => println!("Show list"),
Err(message) => println!("Error: {message}"),
}
}
fn main() {
show(parse("add", Some("Read Rust"), None));
show(parse("list", None, None));
show(parse("add", Some(" "), None));
}
Add: Read Rust
Show list
Error: Empty title
parse only creates a checked command. show displays it; the task list comes in the next lesson. If you call parse("list", Some("extra"), None), the shape check returns an error instead of ignoring the word.
Check your understanding
- Why does
parsereturn a command instead of adding a task immediately? - How does
Nonediffer fromSome("")? - Which function prints an error?
Parsing leaves the list alone; one title is absent and the other was supplied empty; main prints. Rebuild the recall map without looking.
Exercise
Required. Add Done(u32) to the enum for a done action with one number. Parse the number with parse::<u32>(), reject zero and invalid text, and keep the extra-argument check. Try done 2, done 0, and done no. The parser must still not print.
Hint
A new ("done", Some(text), None) arm can call text.parse::<u32>(). Handle Ok(number) and Err(_) with match; return Err for zero.
Reference after attempting
enum Command {
Add(String),
List,
Done(u32),
}
fn parse(action: &str, title: Option<&str>, extra: Option<&str>) -> Result<Command, String> {
match (action, title, extra) {
("list", None, None) => Ok(Command::List),
("add", Some(title), None) => {
if title.trim().is_empty() {
Err(String::from("Empty title"))
} else {
Ok(Command::Add(String::from(title)))
}
}
("done", Some(text), None) => match text.parse::<u32>() {
Ok(0) => Err(String::from("Number must be positive")),
Ok(number) => Ok(Command::Done(number)),
Err(_) => Err(String::from("A number is required")),
},
_ => Err(String::from("Unknown command or argument count")),
}
}
fn show(result: Result<Command, String>) {
match result {
Ok(Command::Add(title)) => println!("Add: {title}"),
Ok(Command::List) => println!("Show list"),
Ok(Command::Done(number)) => println!("Complete: {number}"),
Err(message) => println!("Error: {message}"),
}
}
fn main() {
show(parse("add", Some("Read Rust"), None));
show(parse("list", None, None));
show(parse("add", Some(" "), None));
show(parse("done", Some("2"), None));
show(parse("done", Some("0"), None));
show(parse("done", Some("no"), None));
}
Add: Read Rust
Show list
Error: Empty title
Complete: 2
Error: Number must be positive
Error: A number is required
If you have found a mistake or a typo in this article, tell us about it
Comments (0)
Log in to leave a comment →
No comments yet. Be the first.