Skip to content

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

Easy Primitives

Count enum variants with matches!

Complete count_shapes using matches! predicates. Keep the supplied Shape enum and parser, replace the starter's boolean match, and fill in the three missing counts.

pub fn count_shapes(descriptors: Vec<String>) -> Vec<u64>

Return exactly four counts in this order:

Position Count Include
0 round Every Circle
1 straight Every Square or Rect
2 big Circles whose radius is strictly greater than 10.0
3 unknown Every Unknown

These categories overlap: a big circle also contributes to round. Count each descriptor independently, including duplicates. Empty input returns [0, 0, 0, 0].

For ["circle 5", "circle 20", "square 3", "rect 2 3", "blob"], return [2, 2, 1, 1].

Follow the supplied parser

The parser splits on whitespace and accepts exactly these token sequences:

  • circle followed by one f64 value.
  • square followed by one f64 value.
  • rect followed by two f64 values.

Names are lowercase and case-sensitive. Extra or missing tokens, unknown names and failed numeric conversions produce Unknown. Repeated spaces and leading or trailing whitespace are accepted.

This parser classifies syntax; it does not validate physical dimensions. Negative values and scientific notation are accepted. Rust's f64 parser also accepts NaN and infinity: circle NaN counts as round but not big, while circle inf counts as both. Keep this behavior rather than adding geometric validation.

Turn a pattern into a boolean

Use matches!(value, pattern) when you only need to know whether a pattern matches. For example:

matches!(shape, Shape::Circle(_))

It has the same result as a match whose matching arm returns true and fallback arm returns false. The starter uses that longer form for round; Clippy's match_like_matches_macro flags it under this exercise's warnings gate.

An or-pattern handles the two straight-sided variants:

matches!(shape, Shape::Square(_) | Shape::Rect(_, _))

A guard adds a condition after a successful pattern match. Here shape is a borrowed shape, so the radius binding is a reference:

matches!(shape, Shape::Circle(r) if *r > 10.0)

The boundary is strict: radius 10.0 is not big, and 10.5 is.

Understand the references in filter

shapes.iter() yields &Shape. filter borrows each iterator item when it calls its predicate, so the closure argument s has type &&Shape:

shapes.iter().filter(|s| matches!(s, Shape::Circle(_))).count()

Match ergonomics let these variant patterns work through the references. In the guarded pattern, r has type &f64, so dereference it for the comparison. Each filter examines a borrowed value; it does not consume the shapes vector.

Bindings stay inside the match

matches! can bind r for use in its guard. That binding is not available afterward: the macro returns only a bool. Use if let or match when you need to extract a payload and do more work with it.

The parser's map_or(Shape::Unknown, Shape::Circle) uses the tuple variant constructor as a function: a successful numeric parse becomes Circle(value). You do not need to change that parser to complete the counting predicates.

The solution must compile, return the expected counts and pass clippy -D warnings.