-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Macros: Split in topics, use playpen and cover more topics #193
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,41 @@ | ||
#![feature(macro_rules)] | ||
|
||
macro_rules! create_function { | ||
// This macro takes an argument of "type" `ident` | ||
// The `ident` designator is used for variable/function names | ||
($func_name:ident) => { | ||
// This macro creates a function with name `$func_name` | ||
fn $func_name() { | ||
// The `stringify!` macro converts an `ident` into a string | ||
println!("You called {}()", stringify!($func_name)) | ||
} | ||
} | ||
} | ||
|
||
create_function!(foo) | ||
create_function!(bar) | ||
// Designators provide type safety in macros | ||
//create_function!(0) | ||
// TODO ^ Try uncommenting this line | ||
|
||
macro_rules! print_result { | ||
// The `expr` designator is used for expressions | ||
($expression:expr) => { | ||
// `stringify!` will convert the expression *as it is* into a string | ||
println!("{} = {}", stringify!($expression), $expression) | ||
} | ||
} | ||
|
||
fn main() { | ||
foo(); | ||
bar(); | ||
|
||
print_result!(1u + 1); | ||
|
||
// Remember that blocks are expressions too | ||
print_result!({ | ||
let x = 1u; | ||
|
||
x * x + 2 * x - 1 | ||
}); | ||
} |
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` | ||
* `pat` | ||
* `path` | ||
* `stmt` | ||
* `tt` (*token tree*) is used for operators (`+`, `-`, etc) | ||
* `ty` (*type*) | ||
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. It does make sense to only explain the common ones but having most of them blank here looks really weird. A brief description might be in order. Also, that Macro by example page still doesn't describe them so linking to it won't be of much help. |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Macros can have different implementations depending on their arity (number of | ||
arguments), this can be used to implement some form of function overloading. | ||
Here we implement a macro similar to Python's overloaded | ||
[range](https://docs.python.org/3/library/stdtypes.html?highlight=range#range) | ||
function: | ||
|
||
```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,32 @@ | ||
#![feature(macro_rules)] | ||
|
||
use std::iter::range_step; | ||
|
||
// `macro_rules` behaves like a `match` expression | ||
macro_rules! range ( | ||
// The left side of the fat arrow is the `pattern` side, and the right side | ||
// is the `expansion` side | ||
($end:expr) => { | ||
range(0, $end) | ||
}; | ||
// The macro can have a different expansion for each arity | ||
($start:expr, $end:expr) => { | ||
range($start, $end) | ||
}; | ||
// ^ Each pattern-match arm must be terminated by a semicolon | ||
($start:expr, $end:expr, $step:expr) => { | ||
range_step($start, $end, $step) | ||
}; | ||
// The semicolon on the last arm is optional (is a trailing semicolon) | ||
) | ||
|
||
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) } | ||
} | ||
|
This file was deleted.
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,30 @@ | ||
You can check the expansion of any macro by running the | ||
`rustc --pretty expanded` command over your source file. | ||
|
||
Let's see what the `vec!` macro expands to: | ||
|
||
{vec.rs} | ||
|
||
``` rust | ||
$ rustc --pretty expanded vec.rs | ||
#![feature(phase)] | ||
#![no_std] | ||
#![feature(globs)] | ||
#[phase(plugin, link)] | ||
extern crate std; | ||
extern crate native; | ||
use std::prelude::*; | ||
fn main() { | ||
let v1 = | ||
{ let mut _temp = ::std::vec::Vec::new(); _temp.push(1u); _temp }; | ||
let v2 = | ||
{ | ||
let mut _temp = ::std::vec::Vec::new(); | ||
_temp.push(1u); | ||
_temp.push(2); | ||
_temp | ||
}; | ||
} | ||
``` | ||
|
||
Soon you'll learn how to define macros that work like this one! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
fn main() { | ||
let v1 = vec!(1u); | ||
let v2 = vec!(1u, 2); | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
Macros can be defined to handle indefinite arity (any number of arguments). | ||
This can be accomplished using the *repetition* syntax and the `+` | ||
and `*` operators. This is what the syntax looks like: | ||
|
||
On the pattern side: | ||
|
||
* `($($x:expr),+)`: matches a list (with at least one element) of `expr`s | ||
separated by commas. Examples: `('a', 'b')` and `(1 + 2, 3 * 4, 5 - 6)` | ||
|
||
* `($($y:ident),*)`: matches a list of `ident`s separated by commas, but also | ||
handles the zero arity case. Examples: `()` and `(foo, bar)` | ||
|
||
* Neither of these two patterns will match a list that has a trailing comma. To | ||
allow trailing commas, you can use this pattern: `($($z:expr),+,)` | ||
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. Couple things are odd if you've used regexes before:
I always end up modifying these examples and getting puzzling results so I think expanding the examples would be helpful. I have a PR I'll point to in a little bit to show you what I'm thinking but it's kinda long and perhaps out of place on this page... [EDIT] |
||
|
||
On the expansion side: | ||
|
||
* `[$($x),+]`: will expand the input arguments (the `$x`s) into an array. | ||
Following the previous example: `['a', 'b']` and `[1 + 2, 3 * 4, 5 - 6]` | ||
|
||
For our example, we'll make a `min!` macro that can take any number of | ||
arguments and will return the smallest one. Under the hood it will use the | ||
`min` function that compares *two* arguments. | ||
|
||
{repeat.play} | ||
|
||
If you check the expansion you'll see this expression in the place of the last | ||
macro: | ||
|
||
``` rust | ||
std::cmp::min(5u, std::cmp::min(2u * 3, 4u)) | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
So far we have been using a comma to separate the arguments fed into a macro, | ||
but macros are more flexible than that! You are free to use symbols or words to | ||
separate the input arguments of the macro. | ||
|
||
Let's create a sugary macro to initialize hash maps: | ||
|
||
{separators.play} | ||
|
||
The expansion of the `map!` macro looks like this: | ||
|
||
``` rust | ||
let alphabet = | ||
{ | ||
let mut _temp = std::collections::HashMap::new(); | ||
_temp.insert('a', "apple"); | ||
_temp.insert('b', "banana"); | ||
_temp.insert('c', "carrot"); | ||
_temp | ||
}; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#![feature(macro_rules)] | ||
|
||
macro_rules! map { | ||
($($key:expr => $value:expr),*) => ({ | ||
let mut _temp = std::collections::HashMap::new(); | ||
|
||
$(_temp.insert($key, $value);)* | ||
|
||
_temp | ||
}); | ||
// Handle trailing commas | ||
($($key:expr => $value:expr),+,) => ( | ||
map!($($key => $value),+) | ||
); | ||
} | ||
|
||
fn main() { | ||
let alphabet = map! { | ||
'a' => "apple", | ||
'b' => "banana", | ||
'c' => "carrot", | ||
}; | ||
|
||
println!("{}", alphabet); | ||
} | ||
|
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.
I think it's worth adding
(*identifier*)
here. Same with forexpr
,pat
andstmt
. Note: I actually have no idea whatstmt
is.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.
stmt (statement)
(from IRC)pat (pattern)
(not sure though...asked on IRC and got no reply)matchers (lhs of the => in macro rules)
(from manual)tt (rhs of the => in macro rules)
(from manual;token tree
from IRC)