Idiomatic way to have a ZST that is covariant in all lifetime parameters

Currently when I have a type constructor that is based purely on lifetime parameters and for which is a ZST and for which I'd like the type to be covariant in all the lifetime parameters, I define something like below:

struct Foo<'a, 'b, 'c>(PhantomData<fn() -> (&'a (), &'b (), &'c ())>);

As the quantity of lifetime parameters grows, this gets quite unwieldy. Admittedly I use this almost exclusively in internal code and further when wanting to define a "visitor" for something like serde::de::Visitor, so it's not that big of a deal. Is there something more concise that allows me to treat each lifetime parameter independently (e.g., &'a &'b &'c () would not suffice since that implies 'c: 'b + 'a, 'b: 'a), is Send + Sync, and does not signal to the compiler that the type actually owns anything?

You could make a macro.

macro_rules! covariant_struct {
    ($name:ident { $($lf:lifetime),* $(,)? }) => {
        pub struct $name<$($lf),*>(PhantomData<fn() -> ($(& $lf ()),*)>);
    }
}

covariant_struct! {
    Foo { 'a, 'b, 'c }
}

I don't think you need the fn() part since () is Sync. But maybe you're putting other types there.