#![feature(negative_impls)]
trait A {}
trait B {}
impl<T: ?Sized + A> !B for T {}
impl<T: ?Sized + B> !A for T {}
trait Named {
const NAME: &'static str;
}
impl<T: ?Sized + A> Named for T {
const NAME: &'static str = "Apple";
}
impl<T: ?Sized + B> Named for T {
const NAME: &'static str = "Banana";
}
Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `Named`
--> src/lib.rs:17:1
|
13 | impl<T: ?Sized + A> Named for T {
| ------------------------------- first implementation here
...
17 | impl<T: ?Sized + B> Named for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
For more information about this error, try `rustc --explain E0119`.
error: could not compile `playground` due to previous error
I would argue, the two implementations can't overlap, because implementing A and implementing B is mutually exclusive.
I think neither impl<T: ?Sized + A> !B for T {} nor impl<T: ?Sized + A> Named for T { /* … */ } is related to auto-traits. But I don't really know. It's just a generic implementation, I would say.
Inspired by @Yandros in this post, I could modify the example as follows:
#![feature(specialization)]
trait S {}
trait A: S {}
trait B: S {}
trait Named: S {
const NAME: &'static str;
}
impl<T: ?Sized + S> Named for T {
default const NAME: &'static str = "Apple";
}
impl<T: ?Sized + S + B> Named for T {
const NAME: &'static str = "Banana";
}
fn print_name<T: Named>(_value: T) {
println!("Name is {}.", T::NAME);
}
fn main() {
struct Apple {}
impl S for Apple {}
impl A for Apple {}
struct Banana {}
impl S for Banana {}
impl B for Banana {}
let a = Apple {};
let b = Banana {};
print_name(a);
print_name(b);
}
Compiling playground v0.0.1 (/playground)
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> src/main.rs:1:12
|
1 | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
warning: `playground` (bin "playground") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 1.01s
Running `target/debug/playground`
The following compiles fine and without any warnings, thanks to using with_negative_coherence (since the semantics of negative impls being understood by coherence is precisely what you seem to be looking for, @jbe):
- #![feature(negative_impls)]
+ #![feature(negative_impls, with_negative_coherence)]
trait A {}
trait B {}
impl<T: ?Sized + A> !B for T {}
impl<T: ?Sized + B> !A for T {}
trait Named {
const NAME: &'static str;
}
impl<T: ?Sized + A> Named for T {
const NAME: &'static str = "Apple";
}
impl<T: ?Sized + B> Named for T {
const NAME: &'static str = "Banana";
}
But then you can't actually use the impls apparently (and bad things would probably happen if you found a way to). I.e. this feature is still wonky. (This probably deserves a dedicated issue.)
I haven't had time to play much, but here's some more reading for you. Also, historically, the ability to express mutually exclusive traits has led to putting things on hold until the feature of mutually exclusive traits can receive "more in-depth consideration".
The GAT AlignedRef<'a> allows me to return a reference to unaligned types such as &str but to return a clone of structures that require alignment using the Owned smart-pointer (as the stored data types are coming from LMDB where alignment isn't guaranteed).
I (supposedly) need the negative impl's and negative coherence to later implement Storable on tuples of StorableConstBytesLen types automatically, while being able to implement Storable on other tuples (e.g. tuples of smart pointers where the target is Storable) as well.
A workaround for me is to always declare a newtype for anything that I want to store and then implement Storable on that particular newtype and avoid the generic implementations altogether. I'm not sure what's worse: the extra effort to always implement Storable manually or using a half-baked feature.
Ah okay. I think I should go the safe way then and avoid using it. Possibly the users of my library would then have more boilerplate code, but perhaps I can lessen the impact with macros. Or I implement certain things in my own library directly for certain concrete types instead of using traits.
Thanks for your warning and the (breaking) examples!