Open programmerjake opened 5 months ago
I like the suggestion; however, I worry that having to parse the whole item "manually" may yield a lot of scope creep and effort. A more future-proof, and tractable pattern, following on your idea of impl{}
and type{}
, would be to rely on macro_metavar_exprs to, in a way, implement "fields" in captures. Yielding something along the lines of:
macro_rules! Clone {(
$struct:item_struct
) => (
impl<${struct.generics}>
::core::clone::Clone
for
${struct.name} <${struct.forward_generics}>
where
${struct.where_clauses} // <- these things would expand to smth with trailing `,`
${add_clone_bound!( ${struct.forward_generics} )} // <- eager-expansion pattern
{
fn clone(&self) -> Self {
Self {
${clone_fields!( self, ${struct.field_names} )}
}
}
}
)}
Which would only require simple extra helpers for these eager expansions:
macro_rules! add_clone_bound {(
$($lt:lifetime),* $(,)?
$($T:ident),* $(,)?
$(const $CONST:ident: $ConstTy:ty),* $(,)?
) => (
$(
$T : ::core::clone::Clone,
)*
)}
macro_rules! clone_fields {(
$self:tt $(, $field_name:ident)* $(,)?
) => (
$(
$field_name: ::core::Clone::clone(&self.$field_name),
)*
)}
This reduces the needs for features required for this to work down to two:
:item_struct
kind of matchers, alongside its ${_.member}
getters;${macro_name!(...)}
, for eager expansion (or at least: "arbitrary call-site" expansion)add_clone_bound
is incorrect, const generics can be mixed with type generics. this is part of why I have ${type_params(...)}
also, handling struct and field attributes is commonly needed, e.g. for #[skip]
to skip using some field for derive(Debug)
-style things.
add_clone_bound
is incorrect, const generics can be mixed with type generics. this is part of why I have${type_params(...)}
Good point; that's what I get for tring to do things manually 😅. Agree on the need for thus extra getters/operators (I'd still lean towards fields/methods for the sake of namespacing and whatnot).
also, handling struct and field attributes is commonly needed, e.g. for
#[skip]
to skip using some field forderive(Debug)
-style things.
Yeah, I'm not advocating fully against manually doing things, just that it should not be mandatory 🙂
also, handling struct and field attributes is commonly needed, e.g. for
#[skip]
to skip using some field forderive(Debug)
-style things.Yeah, I'm not advocating fully against manually doing things, just that it should not be mandatory 🙂
I like the idea of just having $v:struct_item
and then a bunch of ${v.struct_meta}
or ${v.field_meta}
(which would have to be 2-deep repetition since there's multiple fields and multiple meta per field).
extending that to handle enums would probably need both $v:enum_item
and $v:enum_variant
since you'd want to pass single variants to an eager macro for some processing.
It would be useful to support
macro_rules!
/macro
-style macros for derive and attribute macros. For derive macros, I think we'll need enough functionality to be able to write aClone
-syle macro so I think we'll want:${ident{$my_member}}
convert any numeric member names to valid identifiers (e.g._1
)). also a variant to match member names that matches following colon too -- so it matches nothing or<ident>:
.{<contents>}
, on expansion the{}
are replaced with the matched delimiters.($delim:paren_or_brace{$($body:tt)*}) => { $delim{inner} ($($body)*) }
would convert:(a, b, c)
to(inner) (a, b, c)
{foo}
to{inner} (foo)
example
Clone
-style derive macro:alternative struct body matching:
maybe it would be better to instead have a matcher for a struct body and have macro expansions for getting field names, field types, field attributes, any where clauses, and building a struct body
or maybe have a matcher for a struct body that pretends all struct bodies are of the
{ a: Ty, b: Ty2 }
variety and transforms the expansion of that matcher to the actual input struct body variety:e.g.: