“This is the 19th day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.
macro_rules! ok_or_return{
// internal rule.
(@error $a:ident,$($b:tt)* )=>{
{
match $a($($b)*) {
Ok(value)=>value,
Err(err)=>{
return Err(err); }}}};// public rule can be called by the user.($a:ident($($b:tt)*))=>{ ok_or_return! (@error $a,$($b)*) }; }fn some_work(i: i64, j: i64) - >Result< (i64.i64), String> {
if i + j > 2 {
Ok((i, j))
} else {
Err("error".to_owned())
}
}
fn main() - >Result< (),String> {
// instead of round bracket curly brackets can also be used
ok_or_return! {some_work(1.4)}; ok_or_return! (some_work(1.0));
Ok(())}Copy the code
Advanced analytical
Macros sometimes perform tasks that require parsing the syntax of the Rust language itself.
Now put together all the concepts we have covered so far. Let’s create a macro to make a structure public with the pub keyword.
First, we need to parse the Rust struct to get the name, fields, and field type of the struct.
Parse name and field
A struct declaration starts with a visibility keyword (such as pub), followed by the keyword of the struct itself, followed by the name of the struct and the contents of the struct:
macro_rules! make_public {
(
// use vis type for visibility keyword and ident for struct name
$vis:vis struct $struct_name:ident { }
) => {
{
pub struct $struct_name{}}}}Copy the code
$vis
And visibility$struct_name
→ Structure name
To make a struct public, we simply return the result of adding the pub keyword and ignore the value before the $VIS variable.
astruct
Multiple fields can contain different data types and visibility.
- The TY tag type is used for data types
- Vis is used for visibility
- Ident is used for the field name
- Zero or more fields are used
*
Repetitive way
macro_rules! make_public {
(
$vis:vis struct $struct_name:ident {
$(
// vis for field visibility, ident for field name and ty for field data type
$field_vis:vis $field_name:ident : $field_type:ty
),*
}
) => {
{
pub struct $struct_name{$(pub $field_name : $field_type,)*
}
}
}
}
Copy the code
Parsing metadata
Typically, structs are attached with some metadata or derived macros (a type of procedure macro), such as #[derive(Debug)]. This metadata needs to be kept complete. Parsing this metadata is done using meta types.
macro_rules! make_public{
(
// meta data about struct
$(#[$meta:meta])*
$vis:vis struct $struct_name:ident {
$(
// meta data about field
$(#[$field_meta:meta])*
$field_vis:vis $field_name:ident : $field_type:ty
),*$(,)+
}
) => {
{
$(#[$meta]) *pub struct $struct_name{$($(#[$field_meta:meta]) *pub $field_name : $field_type,
)*
}
}
}
}
Copy the code
make_public! It’s ready now. To look at make_public! How it works, let’s use macros in Rust Playground for actual compiled code:
fn main(){ make_public! {#[derive(Debug)]
struct Name{
n:i64,
t:i64,
g:i64,}}}Copy the code
The macro extension code is as follows:
macro_rules! make_public {
($ (#[$ meta : meta]) * $ vis : vis struct $ struct_name : ident
{
$
($ (#[$ field_meta : meta]) * $ field_vis : vis $ field_name : ident
: $ field_type : ty), * $ (,) +
}) =>
{
$ (#[$ meta]) * pub struct $ struct_name{$($(#[$ field_meta : meta]) * pub $ field_name : $
field_type,) *
}
}
}
fn main() {
pub struct name {
pub n: i64.pub t: i64.pub g: i64,}}Copy the code
Declare the limitations of macros
Declarative macros have some limitations. Some are related to Rust macros themselves, while others are specific to declarative macros:
- Lack of support for macro auto-completion and extension
- Debugging declarative macros is difficult
- Limited ability to modify
- Larger binaries are generated
- Longer compile times (this applies to both declarative and procedural macros)