1

So I am currently using

rustc 1.36.0-nightly (8dd4aae9a 2019-05-04)

and in my lib.rs I enabled

#![feature(specialization)]

And in a module of my library crate, I have the following, which does not compile. From my understanding it should compile, even without specialization but it does not - in either case.

use std::hash::{Hash,Hasher};


#[derive(Debug, Copy, Clone,PartialEq,Eq)]
struct HashKey<T>(T);

//default 
impl<T : Hash> Hash for HashKey<T> {
    fn hash<H>(&self, state: &mut H) 
        where H : Hasher
    {
        self.0.hash(state)
    }
}

impl Hash for HashKey<f32> {
    fn hash<H>(&self, state: &mut H) 
        where H : Hasher
    {
        unsafe { std::mem::transmute::<f32,u32>(self.0) }.hash(state)
    }
}

impl Hash for HashKey<f64> {
    fn hash<H>(&self, state: &mut H) 
        where H : Hasher
    {
        unsafe { std::mem::transmute::<f64,u64>(self.0) }.hash(state)
    }
}

As we all know, f32 as well as f64 are neither Eq nor Hash because of the NaN problem as discussed for example in this question. Using the unsafe reinterpret_cast<u32> workaround as I do not care about Nan in my case.

So, I have an impl of trait Hash for any HashKey<T> where T : Hash and I have impls for both f32 and f64 which are as pointed out not Hash. As such, it is not even a specialization as those 3 impls cover orthogonal cases.

Despite all that wonderful logic, I get the error:

conflicting implementations of trait `std::hash::Hash` for type `naive::HashKey<f32>`:

conflicting implementation for `naive::HashKey<f32>`

note: upstream crates may add new impl of trait `std::hash::Hash` for type `f32` in future versionsrustc(E0119)
naive.rs(10, 1): first implementation here
naive.rs(18, 1): conflicting implementation for `naive::HashKey<f32>`

Which leads to the questions:

Am I doing something wrong? How to do it right? Why is this a problem in the first place, given those impls refer to sets of types with no overlap?

BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • 1
    Without the specialization feature, this is not expected to compile, for exactly the reason given in the error message. The specialization feature is only incompletely specified and implemented, and currently has bugs and soundness holes; I don't know the exact state of affairs there. I suggest using one of the wrapper that implement `Hash` for floating-point numbers, like the `ordered_float` crate. – Sven Marnach May 10 '19 at 14:08
  • @SvenMarnach But WHY are they conflicting? ``HashKey`` is a different type than ``HashKey`` which is different from all types in the set ``HashKey where T: Hash``. I simply cannot find a state of mind where I can see a conflict. – BitTickler May 10 '19 at 14:21
  • @BitTickler The specialization feature is nowhere near ready to use. I didn't read your question carefully, but my experience of the feature is that it mostly hasn't worked how I hoped. – Peter Hall May 10 '19 at 15:37
  • @BitTickler I think this is the classic "trait you don't own on a type you don't own", the compiler isn't ok with relying on the fact that `f32: !Hash` because that could change. – turbulencetoo May 10 '19 at 15:58
  • @BitTickler It's actually in the error message: "upstream crates may add new impl of trait `std::hash::Hash` for type `f32` in future versions" I did not repeat that, because it was already included in your question. – Sven Marnach May 10 '19 at 16:06
  • @SvenMarnach Sorry, I am not psychic and have no crystal ball either. As such I dismissed that "in the future" part as gibberish. I can only program against the status quo not against a future I cannot foresee. On a more serious note, do they have that hard coded in the compiler? ``if t == "f32" { special_behavior() }``? – BitTickler May 10 '19 at 17:18
  • 1
    @BitTickler No, it applies to any type. The situation that implementing a previously unimplemented trait breaks downstream crates is disallowed in general, to ensure that implementing additional traits is always a backwards-compatible change. This is what the note in the error message tries to explain – sorry for not being clearer. – Sven Marnach May 10 '19 at 17:38
  • To put it yet another way, the compiler tells you that there is a _potential_ conflict due to possible future extensions of an upstream crate, so it conservatively rejects your code. – Sven Marnach May 10 '19 at 17:40
  • @SvenMarnach Now I think I understand. I can impl upstream traits for types in my crate, but not upstream traits for upstream types That makes sense, kind of. Thanks for that explanation. – BitTickler May 11 '19 at 00:08

0 Answers0