How to bound the reference type of a trait?

Hi,

consider the following code example:

trait A {
    fn a(&self) -> i32;
}

trait B {
    fn b(self) -> i32;
}

trait Meta {}
impl<'a, T: 'a> Meta for T where
    T: A,
    &'a T: B {}

I would like to create a meta-trait that combines A and B, so that I can simply bound a type by Meta instead of giving all the trait bounds all the time.
Trait B is a bit special, as it needs to be implemented for the reference of a type.

In the above code example, Meta gets implemented for the right types, but it is not restricted by anything so it cannot be used as a bound.
I know I can bound Meta by A using trait Meta: A {}.
But how do I do the same for B?

How do I specify that for each type T that implements Meta it holds that &T: B?

There isn't a good way to do this. One way introduces a lifetime parameter to Meta. The other way use HRTB, but this prevents some usges, like &&_: Meta for non-'static lifetimes.

trait Meta<'a>: A where &'a Self: B {}
// HRTB
trait Meta: A where for<'a> &'a Self: B {}
1 Like

Thank you, that is very nice! I didn't even know one can use Self in a where clause.

I tried the approach with a lifetime first and experimented how far I get.

Some context: I want to use these kinds of trait bounds to implement an abstraction over a container that allows the container to specify its own iterator types. The iterator type should/has to borrow the container to function.

Here is a minimal (not) working code example:

trait A {
    fn a(&self) -> i32;
}

trait B {
    type ReturnType;

    fn b(self) -> Self::ReturnType;
}

trait Meta<'a> where &'a Self: B, Self: 'a, Self: A {}
impl<'a, T: 'a> Meta<'a> for T where
    T: A,
    &'a T: B {}
    
fn f<'a, Argument: Meta<'a>>(argument: &'a Argument) -> <&'a Argument as B>::ReturnType { // where &'a Argument: B {
    argument.b()
}

(Playground)

From my current understanding, this should work. Meta is bounded by A and for &'a Self it is bounded by B. Nevertheless I get the following error (both on stable and today's nightly):

error[E0277]: the trait bound `&'a Argument: B` is not satisfied
  --> src/lib.rs:16:1
   |
16 | / fn f<'a, Argument: Meta<'a>>(argument: &'a Argument) -> <&'a Argument as B>::ReturnType { // where &'a Argument: B {
17 | |     argument.b()
18 | | }
   | |_^ the trait `B` is not implemented for `&'a Argument`

Adding where &'a Argument: B to f solves the compiler error, but that defies the purpose of Meta.

Am I doing something wrong here, or does the compiler not propagate all the where clause conditions on traits in some cases?

(Edit: Made example minimal)

Another edit: HRTBs

It seems like the same problem persists with HRTBs, see the following example:

trait A {
    fn a(&self) -> i32;
}

trait B {
    type ReturnType;

    fn b(self) -> Self::ReturnType;
}

trait Meta where for<'a> &'a Self: B, Self: A {}
impl<T> Meta for T where
    T: A,
    for<'a> &'a T: B {}

fn f<Argument: Meta>(argument: &Argument) -> <&Argument as B>::ReturnType { // where for<'a> &'a Argument: B {
    argument.b()
}

(Playground)

This time a similar error message again. Both of these errors are solved again by adding the where clause to f.

error[E0277]: the trait bound `for<'a> &'a Argument: B` is not satisfied
  --> src/lib.rs:16:16
   |
11 | trait Meta where for<'a> &'a Self: B, Self: A {}
   | --------------------------------------------- required by `Meta`
...
16 | fn f<Argument: Meta>(argument: &Argument) -> <&Argument as B>::ReturnType { // where for<'a> &'a Argument: B {
   |                ^^^^ the trait `for<'a> B` is not implemented for `&'a Argument`

error[E0277]: the trait bound `&Argument: B` is not satisfied
  --> src/lib.rs:16:46
   |
16 | fn f<Argument: Meta>(argument: &Argument) -> <&Argument as B>::ReturnType { // where for<'a> &'a Argument: B {
   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `B` is not implemented for `&Argument`

Unfortunately, only where bounds on Selfare propogated yet . I forgot about this. You could change B and Meta to

trait B<'a> {
    type ReturnType;

    fn b(&'a self) -> Self::ReturnType;
}

trait Meta<'a>: A + B<'a> {}
impl<'a, T: 'a + A + B<'a>> Meta<'a> for T {}
1 Like

Alright thank you for the fact about the propagation, that really helps my understanding.

I am still not sure if having the lifetime or having an HRTB is the better choice. Is there any list on what HRTBs actually limit? Because without considering these limitations HRTBs actually do seem like the better choice.

You already mentioned something in your first post but to be honest I did not understand that.

Yes. HRTB does limit some use cases. for<'a> &'a T implies that T: 'static, which means you can't really implement Meta for references or anything containing references.

1 Like

Thank you very much. This seems okay for my use case.

And also thank you very much for your help, especially since the explanations you gave me seem ungoogleable for a Rustacean at my current level.

Cheers

1 Like