logo
Gat!() { /* proc-macro */ }
Expand description

Refer to a <Type as Trait>::Assoc<…> type. Or express Trait<Assoc<…> = …> constraints.

Indeed, the GATs defined by #[gat], when outside of a #[gat]-annotated item (or a #[apply(Gat!)]-annotated one), cannot be accessed through the expected <Type as Trait>::Assoc<…> path directly.

In order to work around that limitation, wrapping such paths inside invocations to this very Gat! macro will avoid the issue:

Examples

#[macro_use]
extern crate nougat;

#[gat]
trait LendingIterator {
    type Item<'next>
    where
        Self : 'next,
    ;

    fn next(&mut self)
      -> Option<Self::Item<'_>>
    ;
}

fn first_item<I : LendingIterator> (
    iter: &'_ mut I,
) -> Option< Gat!(<I as LendingIterator>::Item<'_>) >
{
    iter.next()
}

But if you need to annotate a bunch of types like that within the same item (e.g., function, (inline) module), you can, instead, directly #[apply(Gat!)] to that item to automagically get Gat! applied to each and every such type occurrence:

#[macro_use]
extern crate nougat;

#[gat]
trait LendingIterator {
    type Item<'next>
    where
        Self : 'next,
    ;

    fn next(&mut self)
      -> Option<Self::Item<'_>>
    ;
}

#[apply(Gat!)]
fn first_item<I : LendingIterator> (
    iter: &'_ mut I,
) -> Option< <I as LendingIterator>::Item<'_> >
{
    iter.next()
}
Tip: use type aliases!

Granted, the usage of Gat! may make some signature more heavyweight, but the truth is that <Type as Trait>::Assoc<…>, even without a Gat! macro around it, is already quite a mouthful.

And as with any “mouthful type”, the trick is to factor out common patterns with a (generic) type alias.

So it is heavily advisable that GAT-using library authors quickly get in the habit of defining and using them:

type Item<'lt, I /* : LendingIterator */> = Gat!(<I as LendingIterator>::Item<'lt>);
  • #[macro_use]
    extern crate nougat;
    
    #[gat]
    trait LendingIterator {
        type Item<'next>
        where
            Self : 'next,
        ;
    
        fn next(&mut self) -> Option<Self::Item<'_>>;
    }
    
    type Item<'lt, I> = Gat!(<I as LendingIterator>::Item<'lt>);
    
    // Look ma, no macros!
    fn first_item<I: LendingIterator>(iter: &mut I) -> Option<Item<'_, I>> {
        iter.next()
    }

Remarks

Neither Trait::Assoc<…> nor Type::Assoc<…> paths will work, even when the compiler would have enough information to figure out that we are talking of <Type as Trait>, since macros, such as this one, don’t have access to that compiler resolution information, only to syntactical paths.

The only hard-coded exception to this rule is when inside a #[gat] trait definition or implementation: there, not only is Gat! automagically applied where applicable, the Self::Assoc<…> types also become <Self as Trait>::Assoc<…>.