Skip to content

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

Easy Primitives

Literal, range and or-patterns

Classify every character of a string into one of six buckets.

pub fn classify_chars(s: String) -> Vec<String>
bucket members
digit 09
lower az
upper AZ
space space, tab, newline, carriage return
punct the remaining printable ASCII
other everything else, including all non-ASCII

Use only literal, range and or-patterns. No is_ascii_digit, no is_alphabetic, no helper methods. The point is the pattern language.

match stops being a switch statement here

A pattern can be a literal ('q'), an inclusive range ('a'..='z'), a half-open range (..=9, 10..), or several alternatives joined with |:

match c {
    '0'..='9' => "digit",
    ' ' | '\t' | '\n' | '\r' => "space",
    _ => "other",
}

Two things to know about | that people discover late:

It alternates at any nesting depth. Some(1 | 2 | 3) is legal and is what you want — not Some(1) | Some(2) | Some(3). The alternatives sit wherever the choice actually is.

Ranges over char are ranges over Unicode scalar values. 'a'..='z' works because ASCII letters happen to be contiguous. 'a'..='ÿ' compiles and covers a mess. And char has 1,112,064 legal values, so a match on a char built from a handful of ranges is nowhere near exhaustive — the starter proves it. Read what rustc prints:

error[E0004]: non-exhaustive patterns: `'\0'..='\u{8}'`, `'\u{b}'..='\u{c}'`,
              `'\u{e}'..='\u{1f}'` and 5 more not covered

That message is doing real work. It is telling you exactly which slices of the code-point space you forgot, control characters included.

The ASCII layout, because you will need it

0x20 ' '   0x21-0x2F  ! " # $ % & ' ( ) * + , - . /
0x30-0x39  0-9        0x3A-0x40  : ; < = > ? @
0x41-0x5A  A-Z        0x5B-0x60  [ \ ] ^ _ `
0x61-0x7A  a-z        0x7B-0x7E  { | } ~

Punctuation is the four gaps between the alphanumeric blocks. Four ranges, joined with |, in one arm.

Arms are tried in order

The first matching arm wins, and only the first. Overlapping arms are legal and sometimes exactly what you want, but if a later arm is completely covered by an earlier one you get an unreachable_pattern warning — which under -D warnings is a build failure. match_overlapping_arm and almost_complete_range (which spots 'a'..='y' where you almost certainly meant 'z') are the clippy lints in this area.

A lint with a sharp edge, honestly described

manual_range_patterns is on by default and rewrites 1 | 2 | 3 | 4 into 1..=4. Verified on clippy 0.1.95: it fires for integer or-patterns and is silent for char ones — so '0' | '1' | ... | '9' passes the gate even though '0'..='9' is plainly better. Take that as a reminder that a clean clippy run is a floor, not a ceiling. Write the range anyway.

Remember the grade is compile + tests + clippy -D warnings.

Loading visualization…