Skip to content
Learn Rust
Theme

Checkpoint: Standard library

Eight short programs on the Standard library part: text, collections, formatting, files and the environment and process. Earlier parts count too.

How to use it: read the program, write down your answer (the output, or the error code), then open the answer. Every answer is a real run with Rust 1.99.1, edition 2024.

Rustsrc/bin/q1.rs
fn main() {
let csv = "a,b,,c,";
println!("{}", csv.split(',').count());
println!("{}", csv.split_terminator(',').count());
println!("{}", "x\n\ny\n".lines().count());
println!("{}", " a b ".split(' ').count());
println!("{}", " a b ".split_whitespace().count());
}

What are the five numbers?

Answer
terminal
$ cargo run --bin q1
5
4
3
6
2

split keeps every empty piece, the one after the last comma included. split_terminator drops that last one. lines does not count an empty line after the final newline, but keeps the empty line in the middle. split(' ') on " a b " yields six pieces: "", "", "a", "", "b", "". It gives an empty piece wherever two spaces touch and at either end where a space is the first or last character, and split_whitespace skips them. Taught in Cutting text apart.

Rustsrc/bin/q2.rs
use std::cmp::Reverse;
use std::collections::BinaryHeap;
fn main() {
let mut heap = BinaryHeap::new();
for n in [3, 8, 1, 5] {
heap.push(Reverse(n));
}
let Reverse(first) = heap.pop().unwrap();
println!("{first}");
println!("{:?}", heap.peek());
println!("{:?}", heap.into_sorted_vec());
}

What does it print?

Answer
terminal
$ cargo run --bin q2
1
Some(Reverse(3))
[Reverse(8), Reverse(5), Reverse(3)]

A BinaryHeap pops its greatest item, and Reverse flips the comparison, so the first pop is the smallest number, 1, and peek then shows 3. into_sorted_vec sorts ascending by the item’s own order, and for Reverse that puts 8 first. Taught in BinaryHeap.

Rustsrc/bin/q3.rs
use std::fmt;
struct Tag(u32);
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}", self.0)
}
}
struct Name(&'static str);
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.0)
}
}
fn main() {
println!("[{:>6}]", Tag(42));
println!("[{:>6}]", Name("ada"));
println!("[{:>6}]", format!("{}", Tag(42)));
println!("[{:.2}]", Name("ada"));
}

What do the four lines print?

Answer
terminal
$ cargo run --bin q3
[#42]
[   ada]
[   #42]
[ad]

Width, fill and precision are data that each fmt decides how to use. write! ignores them, so the first line has no padding. f.pad applies them, so Name pads and {:.2} cuts it to two characters. Formatting to a String first and padding that String works for any type that implements Display. Taught in Width is your job.

Rustq4.rs
use std::fs::File;
fn main() {
let mut file = File::open("Cargo.toml").unwrap();
let mut text = String::new();
file.read_to_string(&mut text).unwrap();
println!("{}", text.lines().count());
}

File has a read_to_string method. Does this compile? If not, which error code and which import fixes it?

Answer
$ cargo run --bin q4
error[E0599]: no method named `read_to_string` found for struct `File` in the current scope
 --> src/bin/q4.rs:6:10
  |
6 |     file.read_to_string(&mut text).unwrap();
  |          ^^^^^^^^^^^^^^
  |
  = help: items from traits can only be used if the trait is in scope
help: trait `Read` which provides `read_to_string` is implemented but not in scope; perhaps you want to import it
  |
1 + use std::io::Read;
  |
help: there is a method `read_to_end` with a similar name
  |
6 -     file.read_to_string(&mut text).unwrap();
6 +     file.read_to_end(&mut text).unwrap();
  ... (notes pointing into the standard library trimmed)

E0599. The method comes from the Read trait, and you can call a trait’s method as file.read_to_string(..) only where the trait is in scope. The compiler suggests use std::io::Read;, and use std::io::prelude::*; would also do. Taught in Read, Write, BufRead and Seek.

Rustq5.rs
use std::env;
fn main() {
env::set_var("MODE", "test");
println!("{:?}", env::var("MODE"));
}

This program is in edition 2024. Does it compile? If not, which error code?

Answer
$ cargo run --bin q5
error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block
 --> src/bin/q5.rs:4:5
  |
4 |     env::set_var("MODE", "test");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
  |
  = note: consult the function's documentation for information on how to avoid undefined behavior

E0133. In the 2024 edition set_var is an unsafe function, because the environment is one global that other threads may be reading. The fix is an unsafe block with a comment saying why no other thread can be reading. Taught in Environment variables and Why set_var is unsafe.

Rustq6.rs
use std::collections::BinaryHeap;
fn main() {
let readings: Vec<f64> = vec![2.5, 0.5, 1.5];
let heap = BinaryHeap::from(readings);
println!("{}", heap.len());
}

Does it compile? If not, which error code, and which trait is missing?

Answer
$ cargo run --bin q6
error[E0277]: the trait bound `f64: Ord` is not satisfied
 --> src/bin/q6.rs:5:16
  |
5 |     let heap = BinaryHeap::from(readings);
  |                ^^^^^^^^^^ the trait `Ord` is not implemented for `f64`
  |
  = help: the following other types implement trait `Ord`:
            i128
            i16
            i32
            i64
            i8
            isize
            u128
            u16
          and 4 others
  = note: required for `BinaryHeap<f64>` to implement `From<Vec<f64>>`

E0277: f64 has no Ord, because NaN is not equal to anything, and a heap needs a total order. A wrapper whose Ord calls f64::total_cmp is the usual fix. Taught in What the items must provide.

Rustsrc/bin/q7.rs
fn main() {
let word = "héllo";
let start = &word[0..2];
println!("{start}");
}

It should print hé. What happens when it runs, and how do you fix it?

Answer
$ cargo run --bin q7
thread 'main' (191) panicked at src/bin/q7.rs:3:22:
end byte index 2 is not a char boundary; it is inside 'é' (bytes 1..3 of string)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Slicing a str counts bytes, and é takes two. Byte 2 is inside it, so the slice panics. Take characters instead:

Rustq7.rs
fn main() {
let word = "héllo";
let start: String = word.chars().take(2).collect();
println!("{start}");
}
$ cargo run --bin q7
hé

Taught in Cutting at a byte limit and Characters, bytes and what a person sees.

Rustsrc/bin/q8.rs
struct Guard;
impl Drop for Guard {
fn drop(&mut self) {
println!("guard dropped");
}
}
fn main() {
let _guard = Guard;
println!("working");
std::process::exit(3);
}

What does it print, and what exit code does the program end with?

Answer
terminal
$ cargo run -q --bin q8; echo "exit code: $?"
working
exit code: 3

process::exit ends the program at once and runs no destructors, so guard dropped never prints. Returning from main would have run it. The exit code here is 3, the number given (on Unix only the low eight bits are kept, so keep it small). Taught in Exit codes.