Why Sized constraint on Hash impl of *const T?


#1

Hello,

I’m working on wrapping references to create a HashSet that is by reference (for efficiency primarily). I have run into an interesting problem: the Hash implementation for *const T requires that T be sized. This places an undesirable constraint on my library implementation, and makes zero sense to me. Why should it matter what the pointer is pointing at given that we are hashing the pointer itself?!

Is this simply an oversight due to rust’s default Sized constraint? Is there any safe way around this constraint?

Thanks!


#2

Where do you see that? https://doc.rust-lang.org/src/core/hash/mod.rs.html#466 does not require Sized, AFAICT.


#3

T: Sized is an implicit bound that needs to be removed with T: ?Sized. I would guess that the T: Sized bound exists because otherwise the pointer is “wide”, and we didn’t know what to do in that case.

For instance *const [T] is two usizes: a pointer and a length.

This raises the question: should the Hash impl only look at the pointer, or should it also include the length? No one has ever managed to build consensus on what’s correct to do here, so we conservatively just left out the implementation (which will be backwards compatible to add if anyone ever makes a decision).


#4

You’re right of course - I mistakenly looked at the trait itself and forgot about the implied Sized on the impl.

What was the argument, if you recall, for not including length in a fat pointer?


#5

I suppose the workaround is to wrap the pointer in a custom struct that doesn’t require Sized, and then impl Hash for that?


#6

I can’t find the details but iirc the basic idea is “they probably only wanted address equality” (but maybe they didn’t?). Remember this has to be a totally generic impl, so we have to agree that the length of a slice ([T]), the pointer to a vtable (Trait), or any other usage of unsizing we think of should be handled uniformly.


#7

For a slice, I’d think length matters or else two slices starting at same base but of different lengths would hash the same, which is probably not what’s desired.

For the other unsizing with vtable, it’s not as clear, yeah.


#8

Is there any way I can safely get the hash of the entire day pointer? I’ve figured out how to get the hash of just the address by casting first to *const usize and then to usize.


#9

I don’t think there is any way to get the auxiliary data for a fat pointer, not even an intrinsic. So the only way would be an implementation-specific transmute.