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.
1. How many pieces?
Section titled “1. How many pieces?”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
$ 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.
2. Reverse in a heap
Section titled “2. Reverse in a heap”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
$ 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.
3. Does the width apply?
Section titled “3. Does the width apply?”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
$ 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.
4. Which error code?
Section titled “4. Which error code?”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
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.
5. Which error code?
Section titled “5. Which error code?”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
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.
6. Which error code?
Section titled “6. Which error code?”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
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.
7. Fix it: the first two letters
Section titled “7. Fix it: the first two letters”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
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 backtraceSlicing a str counts bytes, and é takes two. Byte 2 is inside it, so the slice panics. Take characters instead:
fn main() { let word = "héllo"; let start: String = word.chars().take(2).collect(); println!("{start}");}hé
Taught in Cutting at a byte limit and Characters, bytes and what a person sees.
8. Does the guard drop?
Section titled “8. Does the guard drop?”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
$ 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.
If you missed one
Section titled “If you missed one”- Questions 1 and 7: Text, then Strings.
- Questions 2 and 6: Collections.
- Question 3: Formatting.
- Question 4: Files and I/O.
- Questions 5 and 8: Arguments, environment, time and processes.
- Next: The standard library tour, or the glossary.
Learn Rust is an independent project, not affiliated with the Rust Project or the Rust Foundation. Code and text are under the MIT licence.Source on GitHub.