We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 17 of 26
matches! and the lints that push you toward it
Parse shape descriptors and count four overlapping categories.
pub fn count_shapes(descriptors: Vec<String>) -> Vec<u64>
Return exactly four counts, in order:
-
round — how many are
Circle -
straight — how many are
SquareorRect -
big — how many are a
Circlewith radius strictly greater than10.0 - unknown — how many failed to parse
Descriptors look like circle 5, square 3, rect 2 3. Anything else,
including a numeric field that does not parse, is Unknown.
The right tool for “is this shaped like X?”
Given a value of a sum type, the question “which variant is this?” comes up
constantly, and a match is a clumsy way to ask it:
match shape {
Shape::Circle(_) => true,
_ => false,
}
That is nine tokens of ceremony around one predicate, and clippy’s
match_like_matches_macro (on by default) will reject it. The tool is:
matches!(shape, Shape::Circle(_))
matches! takes a value and a pattern and gives you a bool. It accepts
everything the pattern language offers:
matches!(s, Shape::Square(_) | Shape::Rect(_, _)) // or-pattern
matches!(s, Shape::Circle(r) if *r > 10.0) // guard
matches!(c, 'a'..='z') // range
which makes it drop straight into a .filter(...) without a closure body.
Your first useful macro
matches! is macro_rules! — an ordinary library macro defined in
std::macros, not a compiler builtin. That is worth noticing early. In many
languages macros are an exotic, dangerous corner; in Rust they are part of
the everyday surface, and vec!, format!, println!, write!, assert!
and matches! are all just library code that takes syntax instead of
values.
Why a macro at all? Because a pattern is not a value. You cannot pass
Shape::Circle(_) to a function — there is no such thing at run time. A
macro operates before type checking, on the syntax, so it can accept a
pattern where an argument would be impossible.
What matches! cannot do
It cannot bind. matches! gives you a bool and nothing else, so the
moment you need the payload it is the wrong tool — reach for if let or a
match. That is not a defect; a predicate that also smuggled a value out
would be a worse predicate.
The neighbours
redundant_pattern_matching (on by default) is the same lesson for
Option and Result: if let Some(_) = o { .. } is o.is_some().
equatable_if_let (nursery, allow-by-default) suggests == in place of
if let where the pattern has no bindings and the type is PartialEq. It
is a stylistic call and reasonable people disagree; the gate does not enforce
it.
manual_is_variant_and (allow-by-default) spots
opt.map(|x| pred(x)).unwrap_or(false) and offers opt.is_some_and(pred).
A small idiom in the parser
Look at r.parse().map_or(Shape::Unknown, Shape::Circle). Shape::Circle is
being passed as a function — a tuple variant’s name is a constructor
function of its payload. Same trick as .map(Some) or .map(Ok).
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.