Step 1 of 7
Reading a line with read_line
Until now your programs did the same thing every time they ran. Real programs react to input: what a user types, or data fed in from a file or another program. The simplest source is standard input, or stdin: the text typed into the terminal, or piped in with my_program < orders.txt. On this site, each test feeds your program its stdin; you'll see it in the Input box next to the expected output.
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 a line");
let name = name.trim();
println!("Hello, {name}!");
}
Ferris
What's your name?
Hello, Ferris!
(The typed name isn't part of the program's output. In a terminal you'd see it because the terminal shows what you type, not because the program prints it.)
Line by line:
use std::io;brings the standard library'sio(input/output) module into scope, so you can writeio::stdin()instead ofstd::io::stdin().uselines go at the top of the file.let mut name = String::new();makes an emptyStringto read into. It must bemut, because reading changes it.io::stdin()gives you a handle to standard input..read_line(&mut name)waits for one line, then appends it toname, including the newline from the Enter key.&mut namelends theStringtoread_linewith permission to change it. Module 10 explains&mut(a mutable reference); for now it's the fixed way to say "read into this String"..expect("failed to read a line")deals with failure. Reading can fail (rarely: the input might not be valid text), soread_linereturns a Result, a value that is either a success or an error..expect(msg)means: "if it failed, stop the program with this message; otherwise carry on". Module 19 covers errors properly.let name = name.trim();removes the newline (and any stray spaces). Shadowing gives the clean version the same name.
Seeing the newline
The debug format {:?} shows a string in quotes with special characters escaped, which makes the newline visible:
use std::io;
fn main() {
let mut line = String::new();
io::stdin().read_line(&mut line).expect("failed to read a line");
println!("{:?}", line);
println!("{:?}", line.trim());
println!("{} bytes", line.len());
}
Ferris
"Ferris\n"
"Ferris"
7 bytes
On Windows, lines end with \r\n; trim() removes that too. Trimming input is a habit worth having everywhere.
Common mistakes
- Forgetting
mut:
use std::io;
fn main() {
let line = String::new();
io::stdin().read_line(&mut line).expect("failed to read a line");
println!("{line}");
}
error[E0596]: cannot borrow `line` as mutable, as it is not declared as mutable
- Forgetting
&mut:read_line(line)gives error E0308,expected `&mut String`, found `String`, and the help line shows where to add&mut. - Forgetting
use std::io;: error E0433 says it can't findio, and suggests the import. - Dropping
.expect(...): it compiles, with a warning:unused `Result` that must be used. Rust doesn't let you silently ignore a possible failure. - Forgetting to trim:
println!("Hello, {name}!")with the untrimmed name prints the!on the next line, because the newline is still insidename.
Your task
Fill in the blanks so the program reads a city name and prints Welcome to <city>!.
Challenge 2
Fill the blanks to get the stdin handle and to lend line to read_line so it can write into it. The program prints the line in capital letters.
Challenge 3
Write the whole program: read a username, then print it (trimmed) and its length in characters:
user: ferris
length: 6
Some tests use names with accents, where bytes and characters differ.