Kullanıcı tanımlı yapıların HashMap veya HashSet gibi koleksiyonlarda kullanılabilmesi için Eq ve Hash (genelde PartialEq de) trait'lerini uygulamak gerekmektedir. Bu trait'lerin uygulanması, nesnelerin birbiriyle nasıl karşılaştırıldığını ve hash değerlerinin nasıl hesaplandığını belirler.
Önemli: Eğer iki nesne eşit ise (a == b), hash değerlerinin de uyuşması gerekmektedir (hash(a) == hash(b)). Aksi takdirde, hash'e dayalı konteynerler düzgün çalışmayacak (bir öğeyi bulmak veya silmek imkansız hale gelecektir).
Uygulama örneği:
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); } }
Bir yapıda bazı alanlar eşitlik karşılaştırmasında (Eq/PartialEq) kullanılırken, bazıları sadece hash hesaplamasında kullanmak mümkün müdür?
Cevap:
Hayır, bu durum invariant'i ihlal eder: İki öğe eşit olarak kabul ediliyorsa, hash'leri de aynı olmalıdır. PartialEq/Eq'de yer alan tüm alanlar, Hash içinde de dikkate alınmalıdır. "Karşılaştırılmayan" alanları göz ardı etmek, sadece bunların karşılaştırma mantığında yer almadığı durumlarda güvenlidir.
impl Hash for MyType { fn hash<H: Hasher>(&self, state: &mut H) { self.x.hash(state); // sadece self.x kullanılıyorsa ve Eq'de de varsa } }
Hikaye
Büyük bir sunucu projesinde, anahtar için yapı
HashMapiçinde kullanılıyordu, ancak hash hesaplamasındaPartialEq'de yer alan bir alan unutuldu. Sonuç olarak, gerekli anahtarın bulunamaması, kullanıcıların önbellekten "sızmasına" yol açtı: öğeler aynı olarak kabul edildi, fakat aslında farklıydılar.
Hikaye
Açık kaynaklı bir kütüphanede geometrik nesneler için
HashveEq'de ondalıklı sayılarla (f64) karşılaştırma yapılmıştı. Sonuç olarak, NaN ile -0.0/+0.0 gibi karşılaştırma özelliklerinden dolayı standart konteynerler hatalarla çalıştı, bazen mevcut bir nesneyi koleksiyonda bulamadı.
Hikaye
Bir fintech projesinde yeniden yapılandırma sırasında, özel alanları olan bir yapı için
#[derive(Hash, Eq, PartialEq)]otomatik türetme kullanıldı ve alanların bildirim sırası değiştirildi. Sonuçta, nihai hash değişti, bu da önbellekleme işlemlerinin bozulmasına ve yeniden başlatma sonrasında veri kayıplarına neden oldu.