We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 19 of 26
Compare weighted records without requiring Eq
Repair the derives on Weighted so the supplied deduplication function compiles. Keep Key's PartialEq, Eq and Hash derives. Weighted should derive PartialEq, which compares both its key and its f64 weight. Preserve the function and comparison behavior rather than adding a custom float-equality policy.
pub fn dedup_keys(keys: Vec<String>, weights: Vec<f64>) -> Vec<String>
Output contract
Pair keys and weights by index, stopping when either vector ends. Ignore unmatched entries in the longer vector. Return each distinct paired key once, in first-seen order. Key comparison is exact and case-sensitive, with no trimming or Unicode normalization. Empty strings and strings containing punctuation or whitespace are valid keys.
For each key, compare every record with that key against its first record. If any comparison is unequal, append one exclamation mark to the key. Otherwise return the key unchanged. Preserve any exclamation marks already present. Different keys can therefore produce identical output strings; do not deduplicate the formatted output again.
keys = ["a", "b", "a"]
weights = [1.0, 2.0, 1.0] -> ["a", "b"]
weights = [1.0, 2.0, 3.0] -> ["a!", "b"]
Either vector being empty produces an empty result. First-seen order comes from the separate vector in the supplied code, not HashSet iteration.
Use ordinary f64 equality
Compare weights with f64's equality, not a tolerance or bit comparison. Positive and negative zero compare equal. Infinities of the same sign compare equal. NaN compares unequal to every f64, including itself.
The supplied scan includes the first record itself. Consequently, a key with a NaN weight gets an exclamation mark even if it occurs only once. A NaN in a later record also flags its key. This follows the existing comparison rule; it is not a test that merely counts duplicate keys.
The JSON fixtures use numeric weights. Standard JSON has no NaN literal; the NaN rule describes direct calls to the Rust function with f64 values.
Why the starter's derives fail
Deriving Eq requires the fields to implement Eq. Deriving Hash similarly requires their Hash implementations. f64 implements neither, so the starter produces E0277 diagnostics for both bounds. Removing only one derive leaves the other problem in place.
Key wraps a String and can derive all three traits. Weighted contains an f64 and can derive PartialEq for the comparisons used here. The HashSet stores references to Key, whose equality and hash depend on the string contents; it does not need to hash Weighted records.
Eq builds on PartialEq
PartialEq provides equality comparisons. For comparisons within one type, its laws require symmetry and transitivity, and != must agree with the negation of ==. Eq adds reflexivity: every value must equal itself.
PartialEq is a supertrait of Eq, so implementing Eq also requires PartialEq. Eq adds no methods, but its bound and law still matter to callers. Deriving Eq alone does not generate PartialEq. See the Eq contract.
NaN breaks reflexivity, which is why ordinary float equality does not qualify. Writing an empty manual Eq implementation would not change that behavior; it would make an invalid promise for this derived Weighted comparison. A different wrapper could define a valid reflexive policy, but then equality and any hashing or ordering implementations would need to agree with it.
HashMap and HashSet require Eq and Hash for their keys; BTreeMap uses Ord. A raw f64 does not meet those key requirements. This exercise avoids that issue by using the String key for membership and comparing weights separately.
Derived equality also respects enum variants
A derived PartialEq considers different variants unequal. Within the same variant, the corresponding fields must compare equal. This complete example demonstrates both rules without assuming a particular machine-code order:
#[derive(PartialEq)]
enum Reading { Value(f64), Missing }
fn main() {
assert!(Reading::Value(2.0) == Reading::Value(2.0));
assert!(Reading::Value(2.0) != Reading::Missing);
assert!(Reading::Value(f64::NAN) != Reading::Value(f64::NAN));
}
Passing requires the specified output, successful compilation and Clippy with warnings treated as errors.
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.