-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand macro section on repetition operators and syntax. #185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
bin/* | ||
stage/* | ||
*~ | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
The arguments of a macro are prefixed by a dollar sign `$` and type annotated | ||
with a *designator*: | ||
|
||
{designators.play} | ||
|
||
This is a list of all the designators: | ||
|
||
* `block` | ||
* `expr` is used for expressions | ||
* `ident` is used for variable/function names | ||
* `item` | ||
* `matchers` (lhs of the => in macro rules) | ||
* `pat` | ||
* `path` | ||
* `stmt` | ||
* `tt` (*token tree*) is used for operators and tokens | ||
* `ty` (*type*) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
Macros can also match and return repeated patterns. This is similar but | ||
different from a regex. `$($x:expr),+` is a matching example. It uses the | ||
`$()` grouping operator to mark `x` as repeatable. `+` or `*` denotes | ||
repetition amount; one or more and zero or more respectively. `,` | ||
is the only separator token available. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use other separators (as long as they are not ambiguous with operators like For example: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to generalize to |
||
|
||
```rust | ||
// The separator only checks separation; this is | ||
// not a regex. | ||
$($x:expr),+ // matches `1, 2, 3` but not `1, 2,` | ||
``` | ||
|
||
When returned, it uses a similar notation: `$($x:expr),+` => `$($x),+` | ||
however, a returned list will not be re-expanded whereas, matching | ||
will find the whole thing. | ||
|
||
```rust | ||
#![feature(macro_rules)] | ||
|
||
macro_rules! echo_broken { | ||
// Attempt to match and return with one compact expression | ||
($($x:expr),+) => { $($x),+ }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not broken for the reason you state "A list is never re-expanded". It's just that the expansion is not valid. This But if you add parentheses to your macro definition, it'll work:
Now I can't think of an useful macro, using this non-recursive form of expansion. You could do something like this:
Now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is interesting because I thought left and right had to match, that is: ($($x:expr),+) => { $($x),+ };
// Valid ^ ^
($($x:expr),+) => { $($x)-+ };
// Invalid ^ ^
// because left and right don't match There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not necessarily. I think the best way to understand these patterns is to read them:
Note that the separator must be between the "elements"
|
||
} | ||
|
||
macro_rules! echo_works { | ||
($x:expr) => { $x }; // one element list | ||
// $x is the first element. `$($y:expr),+` is the tail | ||
($x:expr, $($y:expr),+) => { | ||
// pass the tail to `echo_works` so it can be expanded | ||
($x, echo_works!($($y),+)) | ||
}; | ||
} | ||
|
||
fn main() { | ||
println!("{}", echo_broken!(1u)); // Works for single elements | ||
|
||
// Errors: `,` ignored. A list is never re-expanded so when | ||
// the `,` is reached, the rest of the line is ignored. | ||
// Rust does not allow incomplete expansion, so it errors. To | ||
// handle this, recursion is needed. | ||
//println!("{}", echo_broken!(1u, 2u)); | ||
|
||
println!("{}", echo_works!(1u)); | ||
// Would like it flat but it nests the results... | ||
println!("{}", echo_works!(1u, 2u, 3u, 4u, 5u, 6u)); | ||
} | ||
``` | ||
|
||
Another example making a min macro: | ||
{repeat.play} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Macros can be overloaded to accept different combinations of arguments. | ||
|
||
{overload.play} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Macros allow writing DRY code, by factoring out the common parts of functions | ||
and/or test suites. Here is an example that implements and tests the `+=`, `*=` | ||
and `-=` operators on `Vec<T>`. | ||
|
||
{dry.play} | ||
|
||
``` | ||
$ rustc --test dry.rs && ./dry | ||
running 3 tests | ||
test test::mul_assign ... ok | ||
test test::add_assign ... ok | ||
test test::sub_assign ... ok | ||
|
||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Variadic (variable number of arguments) functions can be implemented via | ||
macros. Here we implement a macro similar to Python's overloaded | ||
[range](https://docs.python.org/3/tutorial/controlflow.html#the-range-function) function: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving #185 (comment) so it's not hidden. Easier to remember.
|
||
|
||
```rust | ||
// Here is how it should work | ||
range!(10i) // Returns 0..9 | ||
range!(3i, 10) // Returns 3..9 | ||
range!(3i, 10, 2) // Returns 3, 5, 7, 9 (3..9 in increments of 2) | ||
``` | ||
|
||
{range.play} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#![feature(macro_rules)] | ||
|
||
use std::iter::range_step; | ||
|
||
// range! will take up to 3 inputs and call range | ||
// function depending on inputs | ||
macro_rules! range ( | ||
($end:expr) => { | ||
range(0, $end) | ||
}; | ||
($start:expr, $end:expr) => { | ||
range($start, $end) | ||
}; | ||
($start:expr, $end:expr, $step:expr) => { | ||
range_step($start, $end, $step) | ||
}; | ||
) | ||
|
||
fn main() { | ||
for i in range!(10i) { print!("{}", i) } | ||
println!(""); | ||
|
||
for i in range!(3i, 10) { print!("{}", i) } | ||
println!(""); | ||
|
||
for i in range!(3i, 10, 2) { print!("{}", i) } | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,13 @@ | |
{ "id": "staging", "title": "Staging Area", "children": [ | ||
{ "id": "bench", "title": "Benchmarking", "children": null }, | ||
{ "id": "ffi", "title": "Foreign Function Interface", "children": null }, | ||
{ "id": "macros", "title": "macro_rules!", "children": null }, | ||
{ "id": "macros", "title": "macro_rules!", "children": [ | ||
{ "id": "arguments", "title": "Arguments and designators", "children": null }, | ||
{ "id": "variable_args", "title": "Variadic macros", "children": null }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove Variadic macros. See #185 (comment) |
||
{ "id": "separators", "title": "Variable separator options", "children": null }, | ||
{ "id": "repetition", "title": "Repetition and expansion", "children": null }, | ||
{ "id": "test_suite", "title": "Generating generic test suite implementations", "children": null } | ||
] }, | ||
{ "id": "rand", "title": "Random", "children": null }, | ||
{ "id": "simd", "title": "SIMD", "children": null }, | ||
{ "id": "test", "title": "Testing", "children": null }, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hide gedit temp files. I can remove it if you want...I just added it because it might be a good idea.
[EDIT]
Let me know if you want this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove it