External type bounds on a trait - make self-contained for usability

I want a trait requiring that a named type can interact with the implementing type (or associated type):

trait ExternalBound
where
    Named: SomeOp<Self>,  // or `SomeOp<Self::Associated>`
{}

This constrains any actual implementation, such that if SomeOp<Self> isn't implemented, the trait can't be implemented:

// impl SomeOp<Target> for Named {}  // Causes compilation error if commented out.
impl ExternalBound for Target {}

But, that doesn't seem to carry forward to actual usages, where the where Named: SomeOp<Self> clause appears to need to be duplicated.

// Causes a compilation error for lack of `where Named: SomeOp<T>`
fn should_be_constrained<T: ExternalBound>(_value: T) {}

But the compilation error has a cryptic note that there may be a better way:
"help: consider introducing a where clause, but there might be an alternative better way to express this requirement"

Is there some way I'm missing to make the trait self-contained such that the basic generic T: ExternalBound is all that is required?

[playground link]

This is a long-standing unfortunate limitation in the compiler: only bounds that have exactly Self on the left — that is, where Self: SomeOtherTrait — are implied.

2 Likes

Oh neat, that had a somewhat recent post which appears to solve it; move the external type into an "unused" internal associated type to carry the bound (so Self::_Named goes on the left):

struct Named;
struct Target;

trait SomeOp<Rhs: ?Sized> {}

trait GivesBoundType {
    type Gives;
    type _Named: SomeOp<Self::Gives>;
}

impl SomeOp<Target> for Named {}

impl GivesBoundType for Target {
    type Gives = Self;
    type _Named = Named;
}

fn uses<T: GivesBoundType>(_value: T) {}

[playground link]

Oh, nevermind, that lifts the bound to the associated type but loses its connection to the original named type. [playground]

Is this what you wanted for that playground?

trait GivesNamed: GivesBoundType<Gives = Target> {}
impl<T: ?Sized> GivesNamed for T where T: GivesBoundType<Gives = Target> {}

fn uses<T: GivesNamed>(value: T) {

Directly naming the associated type in a supertrait would effectively be the same as:

trait GiveDirectlyNamed {
    fn get(&self) -> Target;
}

I need to be able to name a different type for different implementers.

Oh, that one about trait aliases finally sunk in... It requires nightly, but it does carry the non-self where bounds [playground]

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.