Beginner · Types, control flow and functions · topic 9 of 50 · 30 min
Mini project: reading input from the user
Explanation
Why read input?
Programs become useful when they respond to people. In this lesson you build a small interactive tool: a GST calculator that asks for a price. Along the way you meet three ideas you will use everywhere: the standard library, mutable references, and handling errors.
Step 1: bring in the io module
use std::io;std is the standard library that comes with Rust. io is its input/output module. A module is a named group of code; you will create your own in module 7.
Step 2: read a line
let mut text = String::new();
io::stdin()
.read_line(&mut text)
.expect("Failed to read input");- String::new() creates an empty String. String is text that can grow; module 4 explains it fully.
- The variable is mut because read_line changes it.
- &mut text passes a mutable reference: read_line may change our String without taking it away from us. Module 4 is all about references.
- read_line returns a Result, which is either Ok or Err. .expect("...") means: if this is an Err, stop the program with this message. That is fine for now; module 6 shows better ways.
Step 3: turn text into a number
If the user types 250 and presses Enter, text holds "250\n" (on Windows "250\r\n"). The newline would break the conversion, so call trim(), which removes spaces and newlines from both ends. Then parse() converts the text into a number. You must say which number type you want.
let price: u32 = match text.trim().parse() {
Ok(n) => n,
Err(_) => {
println!("Please type a whole number.");
return;
}
};match compares a value against patterns and runs the branch that fits. Here, Ok(n) means "it worked, and the number is n". Err(_) means "it failed"; the underscore ignores the error details. You will study match properly in module 5.
Putting it together
use std::io;
const GST_PERCENT: u32 = 18;
fn main() {
println!("Enter the price in rupees:");
let mut text = String::new();
io::stdin().read_line(&mut text).expect("Failed to read input");
let price: u32 = match text.trim().parse() {
Ok(n) => n,
Err(_) => {
println!("Please type a whole number.");
return;
}
};
let gst = price * GST_PERCENT / 100;
println!("Price: {price}, GST: {gst}, Total: {}", price + gst);
}Asking again in a loop
To keep asking until the input is valid, wrap the reading in a loop and use continue in the Err branch and break in the Ok branch. Remember to create a new String (or call text.clear()) each time, because read_line adds to what is already there.
Common mistakes
- Forgetting trim(). parse sees "250\n" and fails every time.
- Not saying the target type. let n = text.trim().parse().unwrap(); cannot know which number you want; write let n: u32 = ... or parse::<u32>().
- Reusing the same String in a loop without clear(). The second line is appended to the first.
- Using unwrap() on user input. One typo crashes the program; use match.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Mini project: reading input from the user · 1/7
Let us make a program that talks to the user. We will build a small GST calculator.
Prefer a video by a teacher? Find free videos on “rust read user input stdin read_line parse guessing game” on YouTube ↗
In real life: A counter tool at a railway reservation desk
A small terminal tool at a reservation counter asks the clerk for the number of passengers and the fare per head, then prints the total. Clerks make typos, so the tool uses match on parse and asks again instead of crashing, and trims the input so stray spaces do not matter.
use std::io;
fn read_number(prompt: &str) -> u32 {
loop {
println!("{prompt}");
let mut text = String::new();
io::stdin().read_line(&mut text).expect("read failed");
match text.trim().parse::<u32>() {
Ok(n) => return n,
Err(_) => println!("Not a number, try again."),
}
}
}
fn main() {
let people = read_number("Passengers?");
let fare = read_number("Fare per head?");
println!("Total fare: Rs {}", people * fare);
}Practice (0/3 done)
The user types 42 and presses Enter. What is in text after read_line?
Why does this fail to compile? let n = "5".parse().unwrap();
Build a number-guessing game: the secret number is 37 (a constant for now). Read guesses in a loop, print "Too low", "Too high" or "Correct!" and count the attempts. Bad input should print a message and ask again, not crash.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Compound types: tuples and arraysTypes, control flow and functions · same module
- JSON: the language of APIsBackend Development · How the web and backends work
- Reading strings safelyC Programming (from zero to systems) · Strings in C
- Type conversion and reading inputJava Programming (Core to Spring Boot) · Getting started with Java
- Operators, conversions and parsing inputC# and .NET · Getting started with C# and .NET
- Reading input and formatting outputC++ Programming (modern C++ and DSA) · First steps: compiling, output, variables and input
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why is the String passed as &mut text to read_line?
Answer: So read_line can change it while we keep owning it
A mutable reference lets the function change the String without taking ownership.
What does trim() do?
Answer: Removes spaces and newlines from both ends
trim removes whitespace, including the newline from read_line.
What does .expect("msg") do on an Err?
Answer: Stops the program (panics) with that message
expect panics with your message if the Result is an Err.
Which is the best way to handle a typo in user input?
Answer: match on the parse result and ask again
Matching on Ok and Err lets you recover gracefully.
What happens when JSON.parse gets broken text?Related
Answer: It throws a SyntaxError
JSON.parse throws on bad input, so wrap it in try and catch.
What does atoi("abc") return?Related
Answer: 0, indistinguishable from the input "0"
That is why strtol is preferred.
What type does Math.round(2.5) return?Related
Answer: long
Math.round(double) returns a long (Math.round(float) returns int).
What happens with int.Parse("hello")?Related
Answer: Throws a FormatException
Parse throws when the text is not a number. Use TryParse to avoid the crash.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Types, control flow and functions”. The module exam comes next.
- Module 2 exam: Types, control flow and functions · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Ownership: Rust's big idea. 50 topics left in the course before the final exam.