Matching a list of lifetime and type parameters

i am trying to expand the fruity crate's interop abilities with objective-c collections such as NSArray. collections in objective-c are like generics in other languages: type erasure means that the contained objects are not strongly typed, though one can generally assured that all the items in a collection are of a particular class (in objective-c parlance).

still, the fruity crate aims to make things more type safe on the rust side of things. therefore, it would be best for the rust-side NSArray type to be parameterized with a type.

in fruity, there is a macro for declaring objective-c classes called objc_subclass (it subclasses NSObject). this macro matches a definition of the class that aims to inherit from rust struct definition grammar (presumably for ergonomics).

thus, it translates something like pub class MyObject<'a> : NSObject<'a> into an appropriate rust struct definition.

it currently supports lifetimes on the input. i need to extend it to support a generic type, but am having trouble getting the macro to match a generic T.

it is currently written as:

macro_rules! subclass {
         $vis:vis class $a:ident $(<$lifetime:lifetime>)? : $b:ty ;
     ) => {
         $vis struct $a $(<$lifetime>)? ($b);

i have tried replacing $lifetime:lifetime with a number of different patterns, but i get compiler errors of various kinds.

what is the appropriate way to match and then re-use the type parameters? i need to match them optionally, so either zero, one, or two parameters (lifetime and generic type specifier). do i need to replicate the match arm for each one?

It's quite difficult to parse generics specifications with macros by example. The full grammar is specified at Type and lifetime parameters - The Rust Reference .

If you don't want to be able to parse trait bounds, it can be simplified somewhat:

$(<$($lifetime:lifetime,)* $($ty:ident,)*>)?

Note this requires that you terminate each generic lifetime and type with a comma, even the last one. If you don't want that, I think you'll have to split it into two options:

  • $(<$($lifetime:lifetime),+ $(, $ty:ident)*>)?
  • $(<$($ty:ident),*>)?