[Pre-RFC] Scoped `impl Trait for Type`

I did come up with a realistic API where there would be unexpected or outright faulty behaviour:

pub struct ErasedHashSet<S: BuildHasher = RandomState> {
    storage: HashMap<TypeId, Storage<S>, S>,
}

impl<S: BuildHasher> ErasedHashSet<S> {
    pub fn insert<T: Hash + Eq + 'static>(&mut self, value: T) -> Result<(), T> {
        let storage = self.storage.get_mut(&TypeId::of::<T>());
        todo!("Manipulate `storage`.")
    }

    // ...
}

I didn't specify this properly so far, so there are two useful options here:

  1. (not useful) Generic type parameters of functions capture the full set of available scoped implementations. This would make TypeId::of::<_> behave unexpectedly for discrete types, so I'm not considering it here.

  2. Generic type parameters of functions ignore the available scoped implementations at the call site completely. This would cause this API ErasedHashSet to have no way at all to distinguish Hash implementations properly (except heuristically by function pointer, but that's not a solution as there are no equality/inequality guarantees on those), so if RandomState is used, the set can misbehave randomly.

  3. Generic type parameters of functions have identity that's distinguished by available scoped implementations for their bounds only. This distinction carries over when the generic type parameter is used elsewhere, like when calling TypeId::of::<T> with the generic type parameter here.

    I think that is by far the best option here because, while the behaviour may be unexpected in some cases, the unexpected behaviour would at least be deterministic (separating distinct Hash implementations cleanly).

    (This wouldn't limit reflexive implementations since on those, the type parameter is on the implementation rather than the function.)

Option 2 is more complex to explain, but I think that's worth it for getting okay-ish behaviour here.

Edit: Closures and function pointers must use the same rules as generic structs to avoid misbehaviour, I think, so this decision here is only about the generic functions' actual types that I think currently cannot be referred to in at least stable Rust.


Late edit: I found a better way to define the behaviour of 2. that doesn't require special-casing functions and should be less confusing. I'll write that out and update the draft hopefully later today or tomorrow.


Minor edit: There should be an Eq bound on that function, too.
Minor edit: And that second S should be on Storage. Sorry, I'm not great at this without the compiler helping me.
2 days later edit: Clearly I really shouldn't program freehand. S still goes onto the HashMap too. I have a proper version of this with more comments in my revised draft, which should be ready to publish soon-ish depending on how much I can work on it.