Is the following possible ? constrain A::Ty1 == B::Ty2

pub trait Apple_T {
    type Ty1;
    /* other stuff */
}

pub trait Basketball_T {
    type Ty2;
    /* olther stuff */
}

pub struct Foo<A: Apple_T, B: Basketball_T> {
    // constrain it to the case A::Ty1 == B::Ty2
    /*
    other tsuff
     */
}

Is this possible with stable Rust ? If yes, how ? If no, with nightly ?

Kinda possible. See here where I’ve documented a possible pattern a bit. It’s not like the Rust compiler will really “understand” that the types are equal, but at least you can “trick” it into allowing conversions between A::Ty1 and B::Ty2, or SomeTypeContaining<A::Ty1> and SomeTypeContaining<B::Ty2> anyways.

pub trait AppleT {
    type Ty1;
    fn need_mut_ty1_slice(x: &mut [Self::Ty1]);
    /* other stuff */
}

pub trait BasketballT {
    type Ty2;
    fn need_ty2(x: &Self::Ty2);
    /* other stuff */
}

pub struct Foo<A: AppleT, B: BasketballT>
where
    A::Ty1: TypeIsEqual<To = B::Ty2>,
{
    field1: A::Ty1,
    field2: Vec<B::Ty2>,
    /* other stuff */
}

pub trait TypeIsEqual {
    type To: ?Sized;
}
impl<T: ?Sized> TypeIsEqual for T {
    type To = Self;
}

impl<A: AppleT, B: BasketballT> Foo<A, B>
where
    A::Ty1: TypeIsEqual<To = B::Ty2>,
{
    pub fn call_field1_basketball_method(&self) {
        fn helper<S>(s: &S) -> &<S as TypeIsEqual>::To {
            s
        }
        B::need_ty2(helper(&self.field1))
    }
    pub fn call_field2_apple_method(&mut self) {
        fn helper<S>(s: &mut [<S as TypeIsEqual>::To]) -> &mut [S] {
            s
        }
        A::need_mut_ty1_slice(helper(&mut self.field2))
    }
}

Edit: How’d I miss the simpler approach @nerditation gave below :see_no_evil:

pub trait AppleT {
    type Ty1;
    fn need_mut_ty1_slice(x: &mut [Self::Ty1]);
    /* other stuff */
}

pub trait BasketballT {
    type Ty2;
    fn need_ty2(x: &Self::Ty2);
    /* other stuff */
}

pub struct Foo<A: AppleT, B: BasketballT<Ty2 = A::Ty1>>
{
    field1: A::Ty1,
    field2: Vec<B::Ty2>,
    /* other stuff */
}

// still all works this way, too
impl<A: AppleT, B: BasketballT<Ty2 = A::Ty1>> Foo<A, B>
{
    pub fn call_field1_basketball_method(&self) {
        B::need_ty2(&self.field1)
    }
    pub fn call_field2_apple_method(&mut self) {
        A::need_mut_ty1_slice(&mut self.field2)
    }
}
1 Like

I think this should work:

pub struct Foo<A: Apple_T, B: Basketball_T<Ty2 = <A as Apple_T>::Ty1>> {
    //...
}

you may also give name to the unified type (as another generic parameter), but this is changes the public interface:

pub struct Foo<T, A: Apple_T<Ty1 = T>, B: Basketball_T<Ty2 = T>> {
    //...
}
9 Likes

Oh, as is usually best, stop constraining the struct.

Constrain the impls instead, and make an example that actually uses the type parameters in question so people can give you better help.

1 Like

One trait was for a Gang_of_four_pattern. Another trait was for a Gui_Widget. The problem was, to use the two together, I needed to ensure Gang_of_four_pattern::A was the same type as Gui_Widget::B.

The problem with "full context" is that people often respond with answers that involves modifying Gang_of_four_pattern or Gui_Widget -- whereas by removing the context, the only possible solutions are the ones that solves this particular problem.

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.