Jotting down an idea, apologies for brevity.
Motivation: floating-point equality is a massive footgun due to precision loss; you almost always want equality-with-epsilon. Thus, I’d like to prevent floats from being used where they might be tested for equality, e.g. HashMap<f64, V>
.
Introduce $vis impl !$trait for $ty {}
, where $vis != pub
. For all contexts able to see the trait and this impl, the compiler pretends to be unable to prove $ty: $trait
, and reports an error along the lines of “cannot prove trait implementation due to explicit local deimplementation”. Thus, I can write
crate impl !PartialEq for f32 {}
crate impl !PartialEq for f64 {}
in lib.rs
and live happily ever after.
Open questions:
struct K<T: Trait> { .. }
crate impl !Trait for J {}
// downstream ...
let x: K<J> = ..; // should we allow the author of upstream to
// to assume `K<J>` cannot exist?
// Answer: probably not, since local deimplementations mostly
// act as lints.
crate impl !Trait
acts a bit like #[forbid]
, since child modules can’t undo it. Should there be a #[deny]
+ #[allow]
version?
My intuition is that public deimplementations will, without a doubt, lead to exciting problems when one upstream crate depends on T: Trait
, but another publicly deimplements it.