The Problem
As far as I understand, one of the main reasons why !Trait is disallowed,
is because it implies that implementing a trait would be a breaking change.
For example, let's say I'm using a trait Foo from an extern crate:
use foo::Foo;
impl<T: !Foo> Bar for T {
\\ ...
}
let bar: &dyn Bar = &"hello" // doesn't impl foo
If later, the owner of foo implemented Foo for &'static str, then my code would break.
That is because &'static str would no longer be Bar.
This means that implementing traits would be a breaking change.
So to avoid this, we don't allow negative implementations.
A Possible Solution
The way I would solve this issue, is not to forbid !Trait,
but to forbid negative implementations without their complementary counterparts.
So the previous example would not compile, unless I implemented impl<T: Foo> Bar for T.
Thus, T has to be Bar regardless of whether it implements Foo.
Also, if you think about it, impl<T: !Foo> Bar for T has no real expressivity by itself.
It is only useful once you also implement impl<T: Foo> Bar for T,
so Rust can distinguish between two different implementations.
I think this would be a reasonable restriction,
since writing impl<T: !Foo> Bar for T without its complement is no more useful than impl<T> Bar for T.
This should also work with trait arithmetic. So you should be able to do something like:
impl<T: A + !B> Foo for T {
// ...
}
// required by the previous impl block
impl<T: A + B> Foo for T {
// ...
}
Drawbacks
As you increase the number of negative traits,
the number of implementations required to satisfy this rule increases exponentially.
Specifically, the number of separate implementations is 2^n, where n is the number of negative traits.
Also, it seems to be that some details about how to implement !Traits haven't really been fully worked out yet.