How to enforce super trait without implementing the functions?

Viewed 196

I want to be able to say "this trait is a child of that super trait so it should implement all its functions".

The issue is that trait X: Y {} means that Y and X must be implemented. I just want X to be implemented, but making sure all the functions match Y.

Playground link

trait GeneralA {
    fn get_x() -> String;
}

// Issue: cannot do `SpecificA : GeneralA` without also implementing GeneralA, but I want to enforce only having specific implementations, no general or default functions are possible.
trait SpecificA {
    fn get_x() -> String;
}

trait SpecificB {
    fn get_x() -> String;
}

struct S1;

impl SpecificA for S1 {
    fn get_x() -> String {
        "SpecificA->get_x".to_string()
    }
}

impl SpecificB for S1 {
    fn get_x() -> String {
        "SpecificB->get_x".to_string()
    }
}

fn main() {
    dbg!(<S1 as SpecificA>::get_x());
    dbg!(<S1 as SpecificB>::get_x());
}

What I want to describe in the type system:

  • Structs can implement zero or more SpecificX traits.
  • Structs cannot implement the GeneralA trait.
  • All SpecificX traits should have all functions of GeneralA trait.
  • When cast to a trait type, the compiler should error if its not implemented for that struct.

The issue is that Rust expects me to implement GeneralA AND SpecificX.

How can I do this in the type system?

1 Answers

If the "general" version of the trait is not implemented and the "specific" version is, the relationship between them can't be a supertrait relationship. It sounds like what you want instead is a generic trait and some marker types to parameterize it with.

trait HasX<T> {
    fn get_x() -> String;
}

struct A {}

impl HasX<A> for S1 {
    fn get_x() -> String {
        "SpecificA->get_x".to_string()
    }
}

struct B {}

impl HasX<B> for S1 {
    fn get_x() -> String {
        "SpecificB->get_x".to_string()
    }
}

A and B need not ever be instantiated¹ – they only exist to parameterize HasX. (Depending on the nature of your problem, there may already be types that you can use to parameterize HasX; in that case, you don't need to create new ones.)


¹ Instead of empty structs, you can use empty enums (like enum A {}) to enforce they are never instantiated, although this sacrifices some of the flexibility they give you.

Related