We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 21 of 26
Make hashing agree with equality
Fix the hashing implementation so words that differ only in ASCII letter case share a count.
pub fn count_case_insensitive(words: Vec<String>) -> Vec<(String, u64)>
Keep the supplied CiWord(String) wrapper, its ASCII-case-insensitive
PartialEq and Eq, and the HashMap<CiWord, u64>. Replace the derived
Hash with a manual implementation that agrees with that equality rule.
Required result
Treat each input string as one word. Change only ASCII A through Z to
lowercase when grouping words and producing output keys. Preserve all other
characters, including whitespace and punctuation. Do not split or trim strings.
Return one (normalized_word, count) pair per group, sorted by the word using
Rust's String ordering. An empty input returns an empty vector. Empty strings
are valid words and must be counted.
| Input | Output |
|---|---|
["Hello", "HELLO"] |
[("hello", 2)] |
["Zoo", "apple", "APPLE"] |
[("apple", 2), ("zoo", 1)] |
["", ""] |
[("", 2)] |
["Straße", "STRASSE"] |
[("strasse", 1), ("straße", 1)] |
The last pair stays separate because ASCII normalization leaves ß unchanged.
Likewise, "É" and "é" remain distinct. This task does not ask for Unicode
case folding or Unicode normalization.
Why the starter is wrong
Equal keys must produce equal hashes when hashed with the same algorithm and initial state:
a == b implies hash(a) == hash(b)
The starter compares "Hello" and "HELLO" as equal, but its derived Hash
passes their original, differently cased strings to the hasher. The map relies
on hashing to locate candidates before checking equality, so that mismatch
can prevent equivalent words from sharing an entry.
Breaking this contract is a logic error with unspecified results, not undefined behavior. Do not rely on a particular incorrect count to diagnose it. Unequal keys may have the same hash: the map handles those collisions using equality. See the Hash contract.
Hash a consistent representation
Add this implementation to the supplied type and remove #[derive(Hash)]:
use std::hash::{Hash, Hasher};
impl Hash for CiWord {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_ascii_lowercase().hash(state);
}
}
This delegates to the normalized string's Hash implementation. The collection
supplies the hasher; your type supplies the data. Every pair accepted by this
PartialEq produces the same normalized string, so the two implementations
agree. Keep eq_ignore_ascii_case for equality: it compares without allocating
a lowercase copy of each string.
What the checks tell you
Clippy's derived_hash_with_manual_eq lint flags the combination used in the
starter. It identifies a risky implementation pattern; it does not prove that
arbitrary handwritten equality and hashing code agree. The exercise requires
passing compilation, test cases and Clippy with warnings denied.
The existing output code normalizes the stored keys and sorts the vector.
Keep that sort: HashMap iteration order is arbitrary, while this function's
result must be sorted. There is no guarantee that two map iterations or runs
will produce different orders.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.