How to read several values from one line of input
Short answer: line.split_whitespace() hands you the fields one at a time; map each through parse and collect into a Vec — or call next() once per value when the count is known.
From a fresh project — cargo new multi-value — replace src/main.rs with each block below and run it with cargo run. The project name only shows up in cargo’s own output. The transcripts pipe the input in (printf '1 2 3\n' | cargo run -q), so the bytes shown are the bytes the command produces; typing the line by hand ends with the same output.
A line of numbers
Section titled “A line of numbers”use std::io;
fn main() { let mut line = String::new(); io::stdin().read_line(&mut line).unwrap();
let numbers: Vec<i32> = line .split_whitespace() .map(|part| part.parse().unwrap()) .collect();
println!("{numbers:?}");}$ printf '1 2 3\n' | cargo run -q[1, 2, 3]split_whitespace splits on runs of any whitespace — spaces, tabs — and skips the empty stretches, so a line with a double space in it still gives the same three fields. It also swallows the newline read_line left on the end, which is why there is no trim() here: whitespace at either end of the line produces no fields at all. Each field goes through parse, and collect gathers them into the Vec<i32> the annotation asks for.
When the count is known
Section titled “When the count is known”Two numbers on a line — a width and a height — don’t need a Vec. split_whitespace returns an iterator, and next() takes one field off it:
use std::io;
fn main() { let mut line = String::new(); io::stdin().read_line(&mut line).unwrap();
let mut fields = line.split_whitespace(); let width: i32 = fields.next().unwrap().parse().unwrap(); let height: i32 = fields.next().unwrap().parse().unwrap();
println!("area: {}", width * height);}$ printf '3 4\n' | cargo run -qarea: 12next() returns an Option, and it is None once the fields run out. A line with one number panics at the second unwrap() rather than quietly using a wrong value:
$ printf '3\n' | cargo run -q
thread 'main' (5118442) panicked at src/main.rs:9:37:called `Option::unwrap()` on a `None` valuenote: run with `RUST_BACKTRACE=1` environment variable to display a backtraceThe blank first line and the thread id are part of the output; the id changes every run, the message doesn’t.
When a field is not a number
Section titled “When a field is not a number”map(|part| part.parse().unwrap()) turns a bad field into a panic. Collecting into a Result instead moves that decision into a match:
use std::io;
fn main() { let mut line = String::new(); io::stdin().read_line(&mut line).unwrap();
let numbers: Result<Vec<i32>, _> = line.split_whitespace().map(|part| part.parse()).collect();
match numbers { Ok(nums) => println!("sum: {}", nums.iter().sum::<i32>()), Err(e) => println!("not a number: {e}"), }}$ printf '1 2 3\n' | cargo run -qsum: 6$ printf '1 2 x\n' | cargo run -qnot a number: invalid digit found in stringcollect into Result<Vec<_>, _> stops at the first Err and hands the error back, so the whole line is rejected rather than half-parsed. An empty line yields no fields, which makes it Ok with an empty Vec — the sum is 0, not an error.
Why not split(’ ’)
Section titled “Why not split(’ ’)”use std::io;
fn main() { let mut line = String::new(); io::stdin().read_line(&mut line).unwrap();
let by_space: Vec<&str> = line.split(' ').collect(); let by_ws: Vec<&str> = line.split_whitespace().collect();
println!("split(' '): {by_space:?}"); println!("split_whitespace: {by_ws:?}");}$ printf '1 2 3\n' | cargo run -qsplit(' '): ["1", "", "2", "3\n"]split_whitespace: ["1", "2", "3"]split(' ') cuts on exactly one space, so a double space leaves an empty field, and the newline from read_line stays attached to the last one. Neither "" nor "3\n" parses as an i32 — the first fails as Empty, the second as InvalidDigit. split_whitespace has neither problem, which is why it is the one to reach for. How to read a line from standard input has the newline itself; How to split a string has the rest of the family.
- How to read a line from standard input — the
read_linethis page builds on, and the newline it leaves behind - How to split a string —
split,split_whitespace, andsplit_once - Back to Building a command-line tool
Reading standard input until the end — the loop behind a Unix pipe — is the next step in this chapter.