cut: Extract a Field
🎯 cut -d: -f1 /etc/passwd pulls the first colon-separated field (the username) from each line. In Python you'd split and index:
def cut(text: str, delim: str, field: int): # field is 1-based
if field == 0:
raise ValueError("field values may not include zero")
out = []
for line in text.splitlines():
parts = line.split(delim)
if len(parts) >= field:
out.append(parts[field - 1])
return out
assert cut("foo:bar\nbaz:qux", ":", 1) == ["foo", "baz"]
assert cut("foo:bar\nbaz:qux", ":", 2) == ["bar", "qux"]
assert cut("foo:bar\nbaz:qux", ":", 3) == []
assert cut("foo:bar\nbaz:qux", ":", 4) == []
try:
assert cut("foo:bar\nbaz:qux", ":", 0)
except ValueError as e:
assert str(e) == "field values may not include zero"
else:
raise AssertionError("Expected ValueError")
Two cases pull in different directions: a missing field (some lines are too short) is normal, those we skip.
However field == 0 is a bad request. Real cut rejects it: cut: [-bcf] list: values may not include zero. Rust lets you model that split cleanly: one path returns data, the other returns an error.
.nth() returns an Option
Splitting a string gives an iterator over the parts, and .nth(i) pulls the i-th (0-based), returning an Option: Some(part) if it exists, None if there were too few parts. For example:
let mid = "a-b-c".split('-').nth(1); // Some("b")
let off = "a-b-c".split('-').nth(9); // None — out of range, not a crash
That Option is Rust refusing to let you ignore the out-of-range case, where Python could surprise you with an IndexError at runtime.
filter_map drops the misses
filter_map keeps every Some(...) and silently drops the Nones in one pass, exactly the "skip lines without that field" behavior. For example:
let evens: Vec<i32> = ["1", "x", "4"]
.iter()
.filter_map(|s| s.parse().ok())
.collect(); // [1, 4] — "x" failed to parse and was dropped
A typed error, and ok_or to reach it
Model the one invalid input with your own error type. An enum is great for this; it makes the failure modes explicit and testable:
#[derive(Debug, PartialEq)]
enum CutError {
ZeroField,
}
The function now returns Result<Vec<&str>, CutError>. To convert the 0-guard into that error, turn an Option into a Result with Option::ok_or, then ? to bail early:
let n = "abc".find('z').ok_or(CutError::ZeroField)?; // find returns Option
To get that Option in the first place: field is a usize, and subtracting 1 from 0 would underflow rather than go negative. The usize::checked_sub methods on integers return None on underflow instead of panicking — exactly the 0 case you want to reject.
(A plain if field == 0 guard that returns the error early works just as well; checked subtraction is the variant where the underflow case is impossible to forget, and it's what you'll see in the reference solution.)
Login to see the full task and start coding.
Topics
This is a premium exercise
Log in to unlock the full exercise and start coding.
Login to access this exercise