ProgrammatieBackend en Data-ontwikkelaar

Leg uit hoe Hash en Eq worden geïmplementeerd voor eigen types in Rust. Welke fouten kunnen worden gemaakt bij hun implementatie en waarom zijn ze kritisch bij het gebruik in HashMap/HashSet?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Voor het gebruik van gebruikersstructuren in collecties zoals HashMap of HashSet is het noodzakelijk om de traits Eq en Hash te implementeren (vaak ook PartialEq). De implementatie van deze traits bepaalt hoe objecten met elkaar worden vergeleken en hoe hun hash wordt berekend.

Belangrijk: als twee objecten gelijk zijn (a == b), moeten hun hashwaarden overeenkomen (hash(a) == hash(b)). Als dat niet het geval is, zullen containerstructuren die afhankelijk zijn van hashes slecht functioneren (het zal onmogelijk zijn om een element te vinden of te verwijderen).

Voorbeeldimplementatie:

use std::hash::{Hash, Hasher}; #[derive(Eq, PartialEq)] struct Point { x: i32, y: i32, } impl Hash for Point { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); self.y.hash(state); } }

Vraag met een valstrik

Is het mogelijk om Hash te implementeren voor een structuur waarbij sommige velden deelnemen aan de gelijkheidsovereenkomst (Eq/PartialEq) en andere alleen aan hashing?

Antwoord: Nee, dit leidt tot een schending van het invariant: als twee elementen als gelijk worden beschouwd, moet hun hash ook gelijk zijn. Alle velden die deelnemen aan PartialEq/Eq moeten ook worden beschouwd in Hash. Het negeren van "niet-vergelijkbare" velden is alleen veilig als ze niet deelnemen aan de vergelijkingslogica.

impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // als alleen self.x wordt gebruikt in Eq } }

Voorbeelden van echte fouten door onwetendheid over de subtiliteiten van het onderwerp


Verhaal

In een groot serverproject werd een structuur voor de sleutel gebruikt in HashMap, maar bij het hash-eren werd een veld vergeten dat deelnam aan PartialEq. Hierdoor leidde het onvermogen om de juiste sleutel te vinden tot een "lek" van gebruikers uit de cache: elementen werden als gelijk beschouwd terwijl ze verschillend waren.


Verhaal

In een open source-bibliotheek voor geometrische objecten werd voor Hash en Eq gebruikgemaakt van vergelijking met drijvende komma (f64). Hierdoor werkten standaardcontainerstructuren met fouten vanwege de bijzonderheden van de vergelijking van NaN en -0.0/+0.0, en konden ze soms een bestaand object in de collectie niet vinden.


Verhaal

In een fintech-project werd tijdens een refactoring auto-derivatie #[derive(Hash, Eq, PartialEq)] gebruikt voor een structuur met privé-velden, waarbij de volgorde van hun declaratie werd gewijzigd. Hierdoor veranderde de uiteindelijke hash, wat het cachen brak en leidde tot gegevensverlies na herstart.