How to alias a dozen trait bounds?

There are a dozen complex trait bounds which are frequently used. I don't want to write them again and again, how to make an alias of these bounds?

type MapOutput<'a, T> = <T as MapVisit<ApplyGlobalContent<'a>>>::Output;

fn foo<T>()
where
    T: for<'a> MapVisit<ApplyGlobalContent<'a>>,
    for<'a> MapOutput<'a, T>: SpecificUpdate,
    for<'a> <MapOutput<'a, T> as SpecificUpdate>::UpdateTo:
        RenderObject + UpdateWith<MapOutput<'a, T>> + 'static,
{
}

I tried to put them into a trait, but failed.

pub trait ChildrenNodes
where
    // lots of trait bounds
{
}

type MapOutput<'a, T> = <T as MapVisit<ApplyGlobalContent<'a>>>::Output;

impl<T> ChildrenNodes for T
where
    // lots of trait bounds
{
}

// doesn't compile
fn foo<T>()
where
    T: ChildrenNodes
{
}

rustc still requires me to complete the trait bounds.

error[E0277]: the trait bound `for<'a> <T as MapVisit<ApplyGlobalContent<'a>>>::Output: SpecificUpdate` is not satisfied
  --> irisia-core\src\dom\children\set_children.rs:34:8
   |
34 |     T: ChildrenNodes,
   |        ^^^^^^^^^^^^^ the trait `for<'a> SpecificUpdate` is not implemented for `<T as MapVisit<ApplyGlobalContent<'a>>>::Output`
   |
   = help: the following other types implement trait `SpecificUpdate`:
             ()
             ElModelUpdate<'_, El, Pr, Sty, Ch, Oc, Sr>
             once::Once<T>
             repeating::Repeat<I>
             structure::branch::Branch<A, B>
             structure::chain::Chain<A, B>
note: required by a bound in `ChildrenNodes`
  --> irisia-core\src\dom\children\set_children.rs:15:34
   |
12 | pub trait ChildrenNodes
   |           ------------- required by a bound in this trait
...
15 |     for<'a> MapOutput<'a, Self>: SpecificUpdate,
   |                                  ^^^^^^^^^^^^^^ required by this bound in `ChildrenNodes`

I tried macros, but failed too. Macro calls are only allowed at type position.

fn foo<T>()
where
    alias!(T), // doesn't compile, syntax error
{}

Thank anybody for your help.

Can you create a reproducible example?

Instead of putting trait ChildrenNodes where ... you should put trait ChildrenNodes: ... or maybe trait ChildrenNodes<'a>: .... Then you can do T: for<'a> ChildrenNodes<'a>.

1 Like

Sorry but I don't know how to put those trait bounds at Self bound.

This workaround makes foo compiles, but rustc doesn't recognize anything about the trait bound. It forces me to add trait bound on the associated type.

Please look at the playground.

I've put the example to the playground. Please check it out.

As you can see, rustc still requires me to give a further trait bound in this demo that use your second workarond.

Okay, I checked and putting things in where is fine. I think your problems come from &String not implementing AsRef<&str>. The real impl is impl AsRef<str> for String. So what you really want is something like

for<'a> *<Self as MyDeref<&'a String>>::Target: AsRef<str>,

But this is not valid syntax.

I can't think of a way to do this, but maybe someone else can help out if there's a way. But overall, I can't figure out how this is useful, so I don't know a solution.

Oops, I wrote a typo :person_facepalming:. It should be AsRef<str>. Here is the new demo, it's not the key.
What I want is

for<'a> <Self as MyDeref<&'a String>>::Target: AsRef<str>,

Ok I worked it out. The reason is that the trait bound of the alias trait doesn't affect the associated type. Then just define a new associated type. @drewtato

trait MyDeref<T> {
    type Target;
    fn deref(this: T) -> Self::Target;
}

trait AliasDeref<'a>
where
    Self: MyDeref<&'a String, Target = Self::AliasTarget>,
{
    type AliasTarget: AsRef<str>;
}

impl<'a, T> AliasDeref<'a> for T
where
    T: MyDeref<&'a String>,
    <T as MyDeref<&'a String>>::Target: AsRef<str>,
{
    type AliasTarget = Self::Target;
}

Then foo is compiled.

fn foo<T>()
where
    T: for<'a> AliasDeref<'a>,
{
    let string = "hello".to_string();
    T::deref(&string).as_ref();
}

But I must keep the HRTB, or it will cause overflowing when checking trait requirements, and I don't know how to fix this.

1 Like

I just came up with this, but your solution is better.

trait MyDeref<T> {
    type Target;
    fn deref(this: T) -> Self::Target;
}

trait AliasLt<'a>
where
    Self: MyDeref<&'a String>,
{
    type Target: AsRef<str>;
    fn deref(this: &'a String) -> <Self as AliasLt<'a>>::Target;
}

impl<'a, T> AliasLt<'a> for T
where
    T: ?Sized + MyDeref<&'a String>,
    <Self as MyDeref<&'a String>>::Target: AsRef<str>,
{
    type Target = <Self as MyDeref<&'a String>>::Target;
    fn deref(this: &'a String) -> <Self as AliasLt<'a>>::Target {
        <Self as MyDeref::<&'a String>>::deref(this)
    }
}

trait Alias
where
    Self: for<'a> AliasLt<'a>,
{
}

impl<T> Alias for T
where
    T: ?Sized + for<'a> AliasLt<'a>,
{
}

fn foo<T>()
where
    T: Alias,
{
    let string = "hello".to_string();
    <T as AliasLt<'_>>::deref(&string).as_ref();
}

(Playground)

Ah, I guess it's just a limitation of the trait solver.

Only thing I'll say is that you can replace <T as MyDeref<&'a String>>::Target: AsRef<str> with T::Target: AsRef<str>. You only need the <_ as _> syntax if it's not a generic, or if the generic implements multiple traits with the same associated type name.

Oh yes it is. Thanks for reminding me.

1 Like

Your workaround does not meet the requirement completely, because I need a "alias", not another trait. I need to pass a struct meets all the requirements to a function. But thank you all the same.

Don't the blanket implementations take care of this? They ensure that every struct which meets the requirements implements the new trait, without any extra effort on the part of the struct's author.

Uh... I'm both the function's and the struct's author, it's my business to write that "new trait", and which is what I'm asking how to implement. And UpdateWith is not specially serve this function.

You are, basically, hitting the same ages old limitation of trait solver from different directions.

If you know of any other solution except for duplication of bounds for every function with help of macro then I'm all ears.

Oh I missed your reply. Yes, the current situation is when binding a trait, the outer environment will only recognize the rules that bind Self inside the trait, and ignores rules not bind to Self.

Uh, if I knew I wouldn't have this topic created XD. But then again, I'm interested in writing a macro myself if I have time. The idea is to define an associate type for the rules that not related to Self.