New trait solver and simple specialization

Hi! I just gave a shot at the new trait solver in the nightly compiler dated from yesterday with the -Znext-solver rustflag, set in the cargo's config.toml.
Even if specialization is not complete/unsound, I am surprised that this simple case still doesn't compile:

#![feature(specialization)]

trait A<T> {
        fn cool(&self, arg: &T);
}

struct MyStruct;

struct TraitDefault;

struct TraitSpecialized;

trait MarkerTraitForSpecialization {}

trait MarkerTraitForGeneric {}

impl MarkerTraitForSpecialization for TraitSpecialized {
}

impl MarkerTraitForGeneric for TraitDefault {
}

/*
enabling this block instead of the following one yields:
error[E0119]: conflicting implementations of trait `A<_>` for type `MyStruct`
default impl<T: MarkerTraitForGeneric> A<T> for B {
        fn cool(&self, _arg: &T) {

                println!("Generic")
        }
}*/

default impl<T> A<T> for MyStruct {
        fn cool(&self, _arg: &T) {

                println!("Generic")
        }
}

impl<T: MarkerTraitForSpecialization> A<T> for MyStruct {
        fn cool(&self, _arg: &T) {

                println!("Specialized")
        }
}

fn main() {

        let bee = MyStruct;

        let foo = TraitSpecialized;

        let bar = TraitDefault;

        bee.cool(&foo);

        bee.cool(&bar);
}

Error: error[E0277]: the trait bound TraitDefault: MarkerTraitForSpecialization is not satisfied

Traits are well defined here.
Any way to make that work now on nightly (without impl different structs obviously)?

Thanks!

Your snippet works if we mark A<T>::cool as default, instead of the whole impl<T> A<T> for MyStruct. To me, this speaks to the highly unstable state of the specialization feature.

3 Likes

Well spotted, I've put the default at the wrong place. Nice and thank you!

1 Like

Sorry, quick follow up:
Godbolt link

Should I understand that when specialization will be sound, the code I wrote in the link above will be correct? Right now, I am welcomed with "conflicting implementation for MyStruct".

When you uncomment the different parts, it works but the meaning is not the same.

The -Zsolver-next flag doesn't change anything here.

Without the commented parts, the implementations do indeed conflict, as a struct that implements MarkerTraitForSpecialization, MarkerTraitForSpecialization2, and MarkerTraitForSpecialization3 has two non-overridable implementations.

Would a negative bound like

impl<T> A<T> for MyStruct
where
        T: MarkerTraitForSpecialization2 + !MarkerTraitForSpecialization3
{
        /*default*/ fn cool(&self, _arg: &T) {

                println!("Specialized #1")
        }
}

impl<T> A<T> for MyStruct
where
        T: MarkerTraitForSpecialization3 + !MarkerTraitForSpecialization2
{
        fn cool(&self, _arg: &T) {

                println!("Specialized #2")
        }
}

Be of help here? (It's not supported right now apparently)

If coherence took negative bounds into account that far, I think so?

Sounds like you want exclusive traits in some form.

(I don't expect these features to land any time soon.)

1 Like

Thanks quinedot!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.