Skip to content

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

Easy Primitives

match and exhaustiveness: the compiler as a checklist

Turn spreadsheet cell contents into labels. The supplied parser converts each string into a Cell; your task is to fix label using one exhaustive match, with an explicit arm for each variant and no catch-all arm.

pub fn classify(inputs: Vec<String>) -> Vec<String>

Return one label per input, preserving order and duplicates. Empty input produces an empty vector. Keep the supplied parser's behavior.

Parsing and labels

The parser checks these rules in order, without trimming whitespace:

Input rule Variant Output
Empty string Empty empty
Exactly true or false Flag(bool) flag(true) or flag(false)
Starts with = Formula(String) formula(body) with only the first = removed
Parses as an i64 Number(i64) number(value) in ordinary decimal notation
Anything else Text(String) text(count) using chars().count()

Integer parsing accepts ASCII decimal digits with an optional leading + or -, provided the value fits in i64. Leading zeroes are allowed. Spaces, underscores, decimal points and out-of-range values make the input text. Thus +0042 becomes number(42), while 42 becomes text(3).

A formula is preserved as text, not evaluated: =A1+B1 becomes formula(A1+B1), and = becomes formula(). Flags are case-sensitive, so TRUE becomes text(4).

Rust char values are Unicode scalar values, not bytes or user-perceived characters. héllo has five scalars and six UTF-8 bytes. An e followed by a combining acute accent counts as two scalars even if displayed as one accented letter. Use the scalar count for text labels.

Let the patterns describe each case

A pattern such as Cell::Number(number) selects a variant and gives its payload a name. Because label matches a &Cell, bindings to its payloads are references: number is an &i64, and a text binding is an &String. Matching this way does not move the stored strings out of the cell.

Here is a complete example with a smaller enum:

enum Message {
    Empty,
    Text(String),
}

fn describe(message: &Message) -> String {
    match message {
        Message::Empty => "empty".to_string(),
        Message::Text(text) => format!("text({})", text.chars().count()),
    }
}

fn main() {
    let message = Message::Text("héllo".to_string());
    assert_eq!(describe(&message), "text(5)");
    assert_eq!(describe(&message), "text(5)");
    assert_eq!(describe(&Message::Empty), "empty");
}

match is an expression, so describe returns its result directly. The arms must have a compatible result type, allowing Rust's normal coercions. Here each arm produces a String. Returning a number from one arm and a string from another would cause a type error.

Why name every variant?

Remove a case from an otherwise explicit match and rustc reports E0004, identifying the missing pattern. If you later add a variant, compiled matches that no longer cover every possibility also need updating.

A wildcard _ or an unqualified binding can cover all remaining values, so those matches may still compile after the enum changes. Exhaustiveness checks whether cases are covered, not whether each case produces the intended label. This exercise asks you to name all five variants to make that coverage visible.

Fix the starter's first arm

The starter writes Empty instead of Cell::Empty. With no variant imported under that name, Empty introduces a binding that matches any cell. Capitalizing a binding does not turn it into a variant test. The first arm then hides all the arms below it.

Rust's bindings_with_variant_name lint rejects this collision by default, reporting E0170. A misspelling such as Emtpy avoids that specific collision but still creates a catch-all. The later arms then trigger unreachable_patterns, among other warnings; warnings are errors in this exercise's Clippy check.

Use Cell::Empty here. Importing a variant explicitly, for example with use Cell::Empty, can also make a bare name a valid variant pattern. The distinction is whether the name resolves to a variant or introduces a binding.

Clippy can suggest shorter forms such as if let or matches! for some simpler matches. For this task, an explicit five-arm match expresses the five label formats clearly.