Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 9 of 26

Easy Primitives

Literal, range and or-patterns

Classify each Unicode scalar value in a string using match patterns.

pub fn classify_chars(s: String) -> Vec<String>

Return one owned label per value from s.chars(), preserving order and duplicates. Empty input produces an empty vector.

Label Members
digit ASCII 0 through 9
lower ASCII a through z
upper ASCII A through Z
space Space, tab, newline and carriage return only
punct The remaining printable ASCII characters, from ! through ~
other Everything else, including every non-ASCII scalar value

For example, "aA0 é" gives ["lower", "upper", "digit", "space", "other"]. Vertical tab, form feed, DEL and non-breaking space all receive "other". This task's space category is deliberately narrower than Unicode whitespace. A visible character formed from several scalar values gets several labels.

Use literal, range and or-patterns for classification, with _ as the final fallback. Do not replace these patterns with character predicates or guards. Iteration and string-conversion methods are fine. The tests check values; review your code yourself for this pattern-writing requirement.

Recognize values and intervals

A literal pattern matches a specific value, such as 'q'. An inclusive range like 'a'..='z' includes both endpoints. The | operator joins alternatives:

fn break_kind(c: char) -> &'static str {
    match c {
        '\n' | '\r' => "line break",
        _ => "other",
    }
}

fn main() {
    assert_eq!(break_kind('\n'), "line break");
    assert_eq!(break_kind(' '), "other");
}

Ranges can also be one-sided. ..=9 includes every value up to and including nine, while 10.. starts at ten and includes larger values. A bounded exclusive range such as 2..5 includes two, three and four, but not five. These forms are patterns; they do not allocate a collection or iterate over the interval.

fn band(n: i32) -> &'static str {
    match n {
        ..=9 => "below ten",
        10.. => "ten or more",
    }
}

fn main() {
    assert_eq!(band(-5), "below ten");
    assert_eq!(band(9), "below ten");
    assert_eq!(band(10), "ten or more");
    assert!(matches!(4, 2..5));
    assert!(!matches!(5, 2..5));
}

Or-patterns can appear inside other patterns. Both Some(1 | 3) and Some(1) | Some(3) describe the same alternatives. The nested form avoids repeating the surrounding shape:

fn selected(value: Option<i32>) -> bool {
    matches!(value, Some(1 | 3))
}

fn main() {
    assert!(selected(Some(1)));
    assert!(selected(Some(3)));
    assert!(!selected(Some(2)));
    assert!(!selected(None));
}

Use ASCII boundaries for this task

A char range follows numeric Unicode scalar values, not alphabetic order in a language. ASCII letters occupy consecutive blocks, so 'a'..='z' works here. A wider range such as 'a'..='ÿ' also includes punctuation and control values; it is not a test for letters.

The punctuation blocks are:

Inclusive bounds Code points
! through / 0x21 through 0x2F
: through @ 0x3A through 0x40
[ through the backtick character 0x5B through 0x60
{ through ~ 0x7B through 0x7E

Join these four ranges with |. They are disjoint from the digit, letter and four whitespace cases, so those specific arms can appear in any order.

Cover all remaining values

The starter omits punctuation and has no fallback, producing E0004 for a non-exhaustive match. Add the missing punctuation arm and _ => "other". The fallback includes control characters and non-ASCII input. Compiler diagnostics can show uncovered examples or intervals; their exact list and formatting can vary by compiler version.

Match arms are considered in source order. For these unguarded patterns, the first matching arm wins. Put _ last: later arms would be unreachable and the compiler's unreachable_patterns warning would fail the gate. Clippy's match_overlapping_arm can also identify partially overlapping ranges, even when each arm still matches some inputs.

Two other default Clippy warnings can help with range patterns:

  • manual_range_patterns suggests a range for consecutive integer alternatives such as 1 | 2 | 3 | 4.
  • almost_complete_range flags suspicious exclusive ranges such as 'a'..'z', which omit the last letter. Use 'a'..='z' when you intend the complete lowercase ASCII block.

These lints suggest changes; they do not rewrite the submission or prove that its category boundaries are correct. Submissions must compile, pass the tests and pass Clippy with warnings treated as errors.