1.6Reading input: a first look
A program that only prints is a fireworks display: nice to watch, but the conversation is one-way. This lesson adds input, so your programs can ask questions and use the answers. It's also the first lesson where you'll write code containing pieces we can't fully explain yet. That's on purpose, and we'll be honest about exactly which pieces and when each gets paid off.
A program that listens
Here's a program that asks for your name and greets you:
use std::io;
fn main() {
println!("What's your name?");
let mut name = String::new();
io::stdin()
.read_line(&mut name)
.expect("failed to read input");
println!("Hello, {name}!");
}
Run it, type Ada, press Enter, and:
What's your name?
Ada
Hello, Ada
!
It works! Almost. The exclamation mark has fallen off the line, and that small wreck is this lesson's most useful teaching: we'll come back to it in a moment. First, the tour.
Line 1: use std::io; brings the standard library's input/output tools into the program. Treat use lines as the ingredient list at the top of a recipe; chapter 13 explains the pantry system.
let mut name = String::new(); creates a String: an empty, growable container for text. It must be mut, because reading input is going to change it. Why text comes in two flavors (this one, and the string literals you've been printing) is a chapter 5 story.
The three-line statement is the actual read. io::stdin() is a handle to standard input, the stream of whatever the user types into the terminal. .read_line(&mut name) waits for the user to type a line and press Enter, then puts what they typed into name. And .expect("failed to read input") deals with the possibility that reading itself failed: if so, the program stops and prints that message.
What you're taking on credit
Two pieces of that statement are IOUs, and this box is the receipt. The &mut in read_line(&mut name) lends the function permission to modify name without giving the variable away; that's a reference, the star of chapter 9 (lesson 9.2 pays this off exactly). And .expect(...) is the bluntest tool from Rust's error-handling kit, chapter 12 (lesson 12.2). Until those chapters: write the recipe as shown, every time, and don't worry about it. There is no version of this course where you understand everything in week one; there's just the version where we admit it.
Tip
We print the question with println!, on its own line, rather than print!("What's your name? ") on the same line as the typing. That's the 1.5 wrinkle at work: a partial line can sit invisible in the terminal's buffer, leaving the user staring at nothing. Full-line prompts sidestep the problem until chapter 20 hands us the proper fix.
The case of the fallen exclamation mark
So why did ! end up on its own line? Because of a precise and easily-forgotten fact: read_line keeps the Enter. When the user types Ada and presses Enter, the line that arrives is "Ada\n", newline included, and that's what lands in name. Our greeting then prints Hello, + Ada\n + !, and the \n in the middle breaks the line before the ! gets its turn.
The standard cleanup is .trim(), which produces the text with any leading and trailing whitespace (spaces, tabs, and that newline) removed:
use std::io;
fn main() {
println!("What's your name?");
let mut name = String::new();
io::stdin()
.read_line(&mut name)
.expect("failed to read input");
let trimmed = name.trim();
println!("Hello, {trimmed}!");
}What's your name?
Ada
Hello, Ada!
(Putting the trimmed text in a second variable works fine; lesson 5.2 shows the tidier idiom Rust programmers actually use here.)
Two behavioral details to file away, both quiz bait and both real-program bait. First, read_line appends to the string you give it; if name already had text, the new input is added to the end, which is why examples always hand it a fresh String::new(). Second, trimming only removes whitespace at the edges. " Ada Lovelace \n" trims to "Ada Lovelace", inner space intact.
Quiz time
Question #1
The user runs your program and, at the prompt, types two spaces, then 42, then presses Enter. After read_line(&mut input) completes, exactly what text is inside input?
Show solution
" 42\n": both leading spaces and the trailing newline. read_line delivers the line exactly as typed, Enter included; nothing is cleaned up unless you clean it.
Question #2
A classmate deletes the .trim() step because "it worked without it." What two characters of explanation does their next greeting carry, and why?
Show solution
Their greeting will break before the closing punctuation (like our fallen !), because the stored input ends with \n. The newline the user's Enter key produced is part of the string until something removes it.
Question #3
Write a program that asks "What's your favorite color?" and replies with <color> is a great choice. on a single line, regardless of any stray spaces the user types around their answer.
Show solution
use std::io;
fn main() {
println!("What's your favorite color?");
let mut color = String::new();
io::stdin()
.read_line(&mut color)
.expect("failed to read input");
let trimmed = color.trim();
println!("{trimmed} is a great choice.");
}
trim handles both the newline and any accidental spaces, which is exactly the "regardless of stray spaces" requirement.
You can now read text in. Reading numbers in (so you can do math with them) takes one more tool, and it arrives in lesson 5.6. Before any of that: when your growing programs go wrong, the compiler will tell you, at length. The next lesson teaches you to read what it says.