Question
How can I check whether a Rust string token is a semicolon, comma, or colon, increment the relevant counter, and do nothing when it is none of those values?
match token.as_ref() {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => println!("")
}
The current wildcard arm prints an empty line, but many tokens do not match and should simply be ignored. What is the idiomatic Rust approach?
Short Answer
You will learn how Rust's match expression handles a fallback case, how to explicitly perform no action with (), and how to choose between match, if let, and matches! when recognizing selected string values.
Concept
A Rust match expression must be exhaustive: it must define what happens for every value that could reach it. When matching strings, a wildcard pattern, _, is commonly used as the catch-all case.
If unmatched values should be ignored, make that arm evaluate to the unit value:
_ => ()
() is Rust's unit type and unit value. It represents “no meaningful value” and is the normal way to express an intentional no-op in an expression context.
match token.as_ref() {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (),
}
This matters because tokenizers, parsers, event handlers, and data-processing loops often care about only a subset of incoming values. A wildcard arm makes the decision explicit while safely handling everything else.
Mental Model
Think of match as a mail-sorting station:
- Mail marked
";"goes into the semicolon tray. - Mail marked
","goes into the comma tray. - Mail marked
":"goes into the colon tray. - Everything else goes through an “ignore” chute.
The _ => () arm is that ignore chute. It does not print, store, or change anything; it simply acknowledges that other input is expected and should be skipped.
Syntax and Examples
Use _ for a pattern that matches any value not handled by earlier arms. Use () when that case should do nothing.
let mut semicolons = 0;
let mut commas = 0;
let mut colons = 0;
let token = String::from(",");
match token.as_ref() {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (),
}
println!("commas: {commas}"); // commas: 1
token.as_ref() lets Rust view the String as a string slice, &str, so it can be matched against string literals such as ",".
For a one-line no-op arm, this is also valid:
_ => {}
An empty block evaluates to (). In this situation, _ => () is especially clear because it directly shows the unit value.
If you only need to know whether the token is one of several values, matches! can be concise:
if matches!(token.as_ref(), ";" | "," | ":") {
println!("This token is punctuation we track.");
}
However, use match when each matched value needs a different action, such as incrementing different counters.
Step by Step Execution
Consider this loop:
let tokens = ["hello", ";", "world", ",", ":"];
let mut semicolons = 0;
let mut commas = 0;
let mut colons = 0;
for token in tokens {
match token {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (),
}
}
println!(";={semicolons}, ,={commas}, :={colons}");
Execution trace:
tokenis"hello". It matches none of the first three arms, so_ => ()runs. No counter changes.tokenis";". The first arm runs, changingsemicolonsfrom0to1.tokenis"world". The wildcard arm runs. Nothing changes.tokenis",". The comma arm runs, changingcommasfrom0to1.tokenis":". The colon arm runs, changingcolonsfrom0to1.
The final output is:
;=1, ,=1, :=1
Rust checks match arms from top to bottom. Since _ matches everything, it must be the final arm.
Real World Use Cases
Ignoring unmatched values in a match is useful when processing only relevant input:
- Lexers and parsers: count or process punctuation while ignoring identifiers, spaces, and numbers.
- Log analysis: react to
"ERROR"and"WARN"levels while ignoring informational entries. - API data: handle selected status values while leaving unrelated values unchanged.
- User input: recognize a few commands and ignore empty or unsupported input.
- Event processing: respond to particular event variants but intentionally ignore others.
Example: count punctuation from text that has already been split into tokens.
for token in tokens {
match token {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (),
}
}
Real Codebase Usage
In production Rust, a wildcard no-op is often part of a deliberate input-handling policy.
Ignore known-irrelevant values
match token {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (), // Other token types are irrelevant to this pass.
}
A short comment can be valuable if readers may wonder whether ignoring values is intentional.
Validate instead of silently ignoring unknown input
If every value should be recognized, do not use a no-op wildcard. Return an error instead:
fn punctuation_name(token: &str) -> Result<&'static str, String> {
match token {
";" => Ok("semicolon"),
"," => Ok("comma"),
":" => Ok("colon"),
other => Err(format!("unexpected token: {other}")),
}
}
Use guards when a value needs an additional condition
match token {
";" if inside_code_block => semicolons += 1,
"," if inside_code_block => commas += 1,
":" if inside_code_block => colons += 1,
_ => (),
}
The key design decision is whether unmatched input is expected and irrelevant, or unexpected and worth reporting.
Common Mistakes
Printing an empty line does not do nothing
_ => println!("")
This performs output on every unmatched token. It can clutter logs and slow a large loop. Use this instead:
_ => ()
Omitting the fallback arm for &str
This does not compile because match must cover all possible strings:
match token {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
}
Fix it with a wildcard arm:
_ => ()
Putting _ before specific patterns
This makes later arms unreachable because _ matches everything:
match token {
_ => (),
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
}
Put _ last.
Ignoring values that should be errors
A no-op fallback is correct only when other values are genuinely acceptable. If an unknown token indicates malformed input, return a Result or log a useful diagnostic instead.
Forgetting string slices versus owned strings
String literals have type &str, while a String is owned. For an owned String, match a borrowed view without moving it:
match token.as_str() {
";" => semicolons += 1,
"," => commas += 1,
":" => colons += 1,
_ => (),
}
as_ref() can also work when type inference knows the desired reference type, but as_str() is more explicit for String.
Comparisons
| Approach | Best when | Example |
|---|---|---|
match with _ => () | Different recognized values need different actions | Increment a separate counter for each punctuation mark. |
if let | You care about exactly one pattern | if let ";" = token { semicolons += 1; } |
matches! | You only need a true/false membership check | `matches!(token, ";" |
if / else if | A few boolean conditions are clearer than pattern matching | if token == ";" { ... } |
other => ... | You need to inspect, log, or return the unmatched value | other => eprintln!("Unknown: {other}") |
For this counter example, match is the clearest choice because each string literal maps directly to a distinct action.
A wildcard _ differs from a named binding:
_ => () // Ignore the unmatched value.
other => println!("{other}") // Bind and use the unmatched value.
Cheat Sheet
match value {
pattern_one => action_one(),
pattern_two => action_two(),
_ => (), // Match all remaining values and do nothing.
}
matcharms must cover every possible input._is a wildcard pattern: it matches anything.- Place
_after more specific patterns. ()is the unit value and expresses no meaningful result.{}is also an empty no-op block and evaluates to().- Match
Stringvalues against literals withmy_string.as_str(). - Use
other => ...instead of_ => ...when you need the unmatched value. - Use
matches!(value, a | b | c)for a boolean test across multiple patterns.
FAQ
How do I do nothing in a Rust match arm?
Use the unit value:
_ => ()
An empty block, _ => {}, also does nothing.
Why does Rust require _ => () here?
A &str can contain any text, not only the three punctuation strings. Rust requires match to handle all possible values, and _ handles every remaining string.
Is _ => () idiomatic Rust?
Yes. It clearly communicates that all unmatched values are intentionally ignored.
Can I omit the wildcard arm?
Not when matching arbitrary strings. You can omit it only if your patterns already cover every possible value, such as all variants of a small enum.
Should I use as_ref() or as_str() for a String?
Both can work in this context, but as_str() is usually clearer when you specifically want a &str from a String.
How can I match several strings with the same action?
Use pattern alternatives with |:
match token {
";" | "," | ":" => println!("tracked punctuation"),
_ => (),
}
When should unmatched strings produce an error instead?
Return an error when input outside the listed cases is invalid or indicates a bug. Ignore it only when it is normal and irrelevant to the current operation.
Mini Project
Description
Build a small punctuation counter for a token stream. The program examines each token, counts semicolons, commas, and colons, and deliberately ignores all other text. This mirrors a simple first pass in a parser or text-analysis tool.
Goal
Read a list of string tokens and print the count of each tracked punctuation mark without output for ignored tokens.
Requirements
- Create a list containing punctuation tokens and ordinary text tokens.
- Track semicolon, comma, and colon counts with mutable variables.
- Use a
matchexpression to update the correct counter. - Use a wildcard arm that performs no action for all other tokens.
- Print the final counts after processing every token.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.