implied_bounds

Attribute Macro implied_bounds 

Source
#[implied_bounds]
Available on crate features default or proc-macros only.
Expand description

Convenience attribute macro to help one rewrite a trait definition as per the rules described in the documentation of ImpliedPredicate.

Indeed, that trait is very handy, but its usage is not only not the most obvious to write, but more importantly, not very readable afterwards.

Since it is actually a very mechanical operation, it is a good fit for macro automation 🙂

§Example

The following fails to compile:

trait Trait<U: Clone>
where
    Self::Gat<true>: Send,
{
    type Gat<const IS_SEND: bool>;
}

fn demo<T: Trait<U>, U>()
where
    // ❌ Error, missing:
    // U: Clone,
    // T::Gat<true>: Send,
{}
  • Error message:

    Click to show
    error[E0277]: the trait bound `U: Clone` is not satisfied
      --> src/_lib.rs:29:12
       |
    10 | fn demo<T: Trait<U>, U>()
       |            ^^^^^^^^ the trait `Clone` is not implemented for `U`
       |
    note: required by a bound in `Trait`
      --> src/_lib.rs:22:16
       |
    3  | trait Trait<U: Clone>
       |                ^^^^^ required by this bound in `Trait`
    help: consider restricting type parameter `U`
       |
    10 | fn demo<T: Trait<U>, U: std::clone::Clone>()
       |                       +++++++++++++++++++
    
    error[E0277]: `<T as Trait<U>>::Gat<true>` cannot be sent between threads safely
      --> src/_lib.rs:29:12
       |
    10 | fn demo<T: Trait<U>, U>()
       |            ^^^^^^^^ `<T as Trait<U>>::Gat<true>` cannot be sent between threads safely
       |
       = help: the trait `Send` is not implemented for `<T as Trait<U>>::Gat<true>`
    note: required by a bound in `Trait`
      --> src/_lib.rs:24:22
       |
    3  | trait Trait<U: Clone>
       |       ----- required by a bound in this trait
    4  | where
    5  |     Self::Gat<true>: Send,
       |                      ^^^^ required by this bound in `Trait`
    help: consider further restricting the associated type
       |
    11 | where <T as Trait<U>>::Gat<true>: Send
       |       ++++++++++++++++++++++++++++++++

You can easily fix this by slapping the #[implied_bounds] attribute on it:

#[::implied_bounds::implied_bounds] // 👈
trait Trait<U: Clone>
where
    Self::Gat<true>: Send,
{
    type Gat<const IS_SEND: bool>;
}

fn demo<T: Trait<U>, U>()
where
    // OK ✅
{}

This shall not change anything for implementors (they have to abide by the provided bounds/clauses/predicates no matter whether the #[implied_bounds] attribute is used or not, and it shall suffice).

§How does the macro work

  • Tip: you can provide the debug arg to the attribute for it to highlight the non-implied clauses it shall rewrite:

    //                👇
    #[implied_bounds(debug)]
    trait ...

The attribute identifies the non-implied clauses (bounds on generic type parameters, as well as where clauses where the left-hand-side (bounded type) is not Self), and rewrites them using ImpliedPredicate, like this:

use ::implied_bounds::*;

#[implied_bounds] // 👈
trait Trait<U: Clone>
where
    Self::Gat<true>: Send,
{
    type Gat<const IS_SEND: bool>;
}

becomes:

use ::implied_bounds::*;

trait Trait<U>
:
    ImpliedPredicate<U, Impls: Clone> +
    ImpliedPredicate<Self::Gat<true>, Impls: Send> +
{
    type Gat<const IS_SEND: bool>;
}

where ImpliedPredicate is a trivially-true / always-holding trait clause, on condition that it be well-formed, i.e., on condition that the bounds on its Impls associated type do hold, wherein its Impls associated type is defined to always be the same as the generic arg fed to it:

                 X    :    Bounds…
                      ⇔
ImpliedPredicate<X, Impls: Bounds…>