Skip to content

Commit

Permalink
Explain how to parse methods.
Browse files Browse the repository at this point in the history
I had to do this myself recently and when I looked in this chapter it
was "WIP" so I had to work it out myself. Might as well record it for
the benefit of others.
  • Loading branch information
nnethercote authored and Veykril committed Feb 8, 2024
1 parent 9d6fa65 commit 9abe6dd
Showing 1 changed file with 81 additions and 2 deletions.
83 changes: 81 additions & 2 deletions src/decl-macros/building-blocks/parsing.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ macro_rules! function_item_matcher {
$( #[$meta:meta] )*
// ^~~~attributes~~~~^
$vis:vis fn $name:ident ( $( $arg_name:ident : $arg_ty:ty ),* $(,)? )
// ^~~~~~~~~~~~~~~~argument list!~~~~~~~~~~~~~~^
// ^~~~~~~~~~~~~~~~argument list~~~~~~~~~~~~~~~^
$( -> $ret_ty:ty )?
// ^~~~return type~~~^
{ $($tt:tt)* }
Expand Down Expand Up @@ -53,7 +53,86 @@ Kind of like a `Derive` proc-macro but weaker and for functions.

The macro for parsing basic functions is nice and all, but sometimes we would like to also parse methods, functions that refer to their object via some form of `self` usage. This makes things a bit trickier:

> WIP
```rust
macro_rules! method_item_matcher {
// self
(
$( #[$meta:meta] )*
// ^~~~attributes~~~~^
$vis:vis fn $name:ident ( $self:ident $(, $arg_name:ident : $arg_ty:ty )* $(,)? )
// ^~~~~~~~~~~~~~~~~~~~~argument list~~~~~~~~~~~~~~~~~~~~~~^
$( -> $ret_ty:ty )?
// ^~~~return type~~~^
{ $($tt:tt)* }
// ^~~~~body~~~~^
) => {
$( #[$meta] )*
$vis fn $name ( $self $(, $arg_name : $arg_ty )* ) $( -> $ret_ty )? { $($tt)* }
};

// mut self
(
$( #[$meta:meta] )*
$vis:vis fn $name:ident ( mut $self:ident $(, $arg_name:ident : $arg_ty:ty )* $(,)? )
$( -> $ret_ty:ty )?
{ $($tt:tt)* }
) => {
$( #[$meta] )*
$vis fn $name ( mut $self $(, $arg_name : $arg_ty )* ) $( -> $ret_ty )? { $($tt)* }
};

// &self
(
$( #[$meta:meta] )*
$vis:vis fn $name:ident ( & $self:ident $(, $arg_name:ident : $arg_ty:ty )* $(,)? )
$( -> $ret_ty:ty )?
{ $($tt:tt)* }
) => {
$( #[$meta] )*
$vis fn $name ( & $self $(, $arg_name : $arg_ty )* ) $( -> $ret_ty )? { $($tt)* }
};

// &mut self
(
$( #[$meta:meta] )*
$vis:vis fn $name:ident ( &mut $self:ident $(, $arg_name:ident : $arg_ty:ty )* $(,)? )
$( -> $ret_ty:ty )?
{ $($tt:tt)* }
) => {
$( #[$meta] )*
$vis fn $name ( &mut $self $(, $arg_name : $arg_ty )* ) $( -> $ret_ty )? { $($tt)* }
}
}

# struct T(i32);
# impl T {
# method_item_matcher!(
# #[inline]
# pub fn s(self, x: i32) -> String { format!("{}", x) }
# );
# method_item_matcher!(
# pub fn ms(mut self, x: i32,) -> String { format!("{}", x) }
# );
# method_item_matcher!(
# pub fn rs(&self, x: i32, y: i32) -> String { format!("{}", self.0 + x + y) }
# );
# method_item_matcher!(
# pub fn rms(&mut self) -> String { self.0.to_string() }
# );
# }
#
# fn main() {
# assert_eq!({ let t = T(11); t.s(11) }, "11");
# assert_eq!({ let t = T(22); t.ms(22) }, "22");
# assert_eq!({ let t = T(30); t.rs(1, 2) }, "33");
# assert_eq!({ let mut t = T(44); t.rms() }, "44");
# }
```
The four rules are identical except for the `self` receiver on both sides of the rule, which is `self`, `mut self`, `&self`, and `&mut self`.
You might not need all four rules.

`$self:ident` must be used in the matcher instead of a bare `self`.
Without that, uses of `self` in the body will cause compile errors, because a macro invocation can only access identifiers it receives from parameters.

## Struct

Expand Down

0 comments on commit 9abe6dd

Please sign in to comment.