4 ms·
The issue with NaN equality is interesting, but is that really why they're adding a clear(x) builtin? What if you want to remove a single-NaN key from a map? cl
by halpmeh 4y ago
The issue with NaN equality is interesting, but is that really why they're adding a clear(x) builtin? What if you want to remove a single-NaN key from a map? clear(x) seems like a band-aid at best if the Go team is strictly trying to fix removing NaN keys in maps.
- erk__ 4y agoThe way Rust dels with this issue is by saying that the Key must implement both `Hash` and `Eq` where Eq is a strict equality compared to `PartialEq` which allows the equality relation to be incomplete. While integers implement both PartialEq and Eq, floating point values only implement PartialEq. https://doc.rust-lang.org/std/collections/struct.HashMap.html https://doc.rust-lang.org/std/collections/struct.HashMap.htm... https://doc.rust-lang.org/std/cmp/trait.Eq.html https://doc.rust-lang.org/std/cmp/trait.Eq.html https://doc.rust-lang.org/std/cmp/trait.PartialEq.html https://doc.rust-lang.org/std/cmp/trait.PartialEq.html There is also a partial ordering relation `PartialOrd` (and `Ord` for complete relations) Floating point values only implement the partial relation as well because of NaN's. This means that it is a bit harder to sort them, though the floating point standard also has a seperate complete comparison relation with some extra rules which you can use instead. https://doc.rust-lang.org/std/primitive.f64.html#method.total_cmp https://doc.rust-lang.org/std/primitive.f64.html#method.tota...
- duped 4y agoThis is one of those rough edges where Rust is more annoying than helpful imo. It's also inconsistent. I'd rather panic on NaN comparisons than complicate the fundamental comparison traits. Treat it like integer overflow or array bounds access. We don't have different traits for indexing that might fail or overflow that might happen. There are methods on the trait to handle those cases when you care about them. But that said, this feels like an XY issue. If you're comparing hashes of floats as keys somewhere you don't want IEEE 754 defined equivalence. You almost certainly want bit equivalence, which means the keys should be type punned to u32/u64. Classic example is memoizing function calls with floats. You don't want to compare the semantic values of the arguments, but the literal bits. For floats that's not the same.
- moomin 4y agoNo you don’t, you want to use the hardware defined equality function. And that one is IEEE 754 compliant.
- anonymoushn 4y agoHow are you so sure what other people want? Using the hardware defined equality function for integers is reasonable a lot of the time.
- duped 4y agoMy point is sometimes I want NaN to compare with NaN as true when I'm using a float as a key in a table.
- tialaramex 4y agoYou are welcome to build a type which has this property and declares that it is Eq. But Rust's built-in floating point types f32 and 64 do not have that property.
- jeroenhd 4y agoIn a hash table, I want my equality function to be based on the actual value, not the super special comparison for a specific type. If I write a UniqueNumber that just returns false on the == operator, I'd be stupid but the hash map should still work. Java has a separate getHashCode() and equals() method for a reason. Either provide a hash key override or use the raw bits for deep compares. Custom equality algorithms usually don't make sense for arbitrary key-value stores.
- tialaramex 4y agoBecause of the Pigeonhole Principle what you're describing is nonsense. In Rust you can make UniqueNumber, indeed misfortunate::OnewayGreater is such a type. misfortunate::Maxwell even more so. However of course the HashMap can't meaningfully "work" for this type. Rust promises your program doesn't have Undefined Behaviour despite such a prank, but it might spin forever when you try to insert this type into a map for example, a well defined but undesirable behaviour brought on by your poor choices. You seem to imagine that Java's hash map doesn't need to compare types, that it can just use getHashCode() -- however because of the Pigeonhole Principle that can't actually work. And NaN != NaN isn't a "custom equality algorithm" it's literally how the floating point numbers are defined in your CPU.
- tialaramex 4y agoRust's HashMap and similar containers have clear() and many of them have drain() Drain is interesting, because it resolves the other problem if things are in the collection which can never be retrieved from it. Drain gives you each of the things in the collection, one at a time, to do with as you wish, the collection is now empty. So this means even if the collection has a dozen SillyNonsenses in it, all of which insist they're not the one you were looking for whenever you go looking for a SillyNonsense in the collection, you get all twelve of them out in Drain, and can examine them as you wish. Of course if you just wanted to fish one thing out and then throw the rest away, you can do that, once you drop the result of Drain the rest of the things being drained are dropped.
- iudqnolq 4y agoI thought HashMap behaved in an undefined-but-safe manner if an implementation of Hash or Eq is incorrect. Are you saying there's a reasonable way to put misbehaving types in a HashMap? (I'm pretty sure I recognize your username as someone with way more rust experience than me. For the crowd an implementation of Eq for floats that used the standard comparison operators would be "incorrect", which is why the stdlib doesn't provide one)
- tialaramex 4y agoIf your key type K doesn't obey the rules explained in HashMap ("if two keys are equal, their hashes must be equal") then Rust doesn't promise HashMap<K,V>'s functions will do something useful (although it will still be safe if your type is safe). So no this isn't a license to put silly things in hash maps. Non-useful but safe consequences might include spinning indefinitely. But if you have put something daft in a hash map, drain() should definitely get it back out, so there's that.