|
|
@@ -0,0 +1,342 @@ |
|
|
|
#![allow(unused)] |
|
|
|
//#![recursion_limit = "1000000000"] |
|
|
|
|
|
|
|
//! # Examples |
|
|
|
//! |
|
|
|
//! ```compile_fail |
|
|
|
//! macro_rules! ah_ah_ah_didnt_say_the_magic_word { |
|
|
|
//! ($x:ident) => { |
|
|
|
//! let x_mut_1 = &mut $x; |
|
|
|
//! let x_mut_2 = &mut $x; |
|
|
|
//! *x_mut_1 += 1i32; |
|
|
|
//! } |
|
|
|
//! } |
|
|
|
//! let mut x: i32 = 0; |
|
|
|
//! ah_ah_ah_didnt_say_the_magic_word!(x); |
|
|
|
//! ``` |
|
|
|
//! |
|
|
|
//! Generic `min` won't compile: |
|
|
|
//! |
|
|
|
//! ```compile_fail |
|
|
|
//! fn min<T>(a: T, b: T) -> T { |
|
|
|
//! if a < b { a } else { b } |
|
|
|
//! } |
|
|
|
//! ``` |
|
|
|
//! |
|
|
|
//! ```compile_fail |
|
|
|
//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } |
|
|
|
//! let b: std::collections::HashMap<String, u32> = Default::default(); |
|
|
|
//! add_one!(b); |
|
|
|
//! ``` |
|
|
|
//! |
|
|
|
//! ```compile_fail |
|
|
|
//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } |
|
|
|
//! add_one(42i32); |
|
|
|
//! ``` |
|
|
|
//! |
|
|
|
//! ```compile_fail |
|
|
|
//! macro_rules! make_adder_fn { |
|
|
|
//! ($n:expr) => { |
|
|
|
//! fn add_$n(rhs: i32) -> i32 { |
|
|
|
//! $n + rhs |
|
|
|
//! } |
|
|
|
//! } |
|
|
|
//! } |
|
|
|
//! make_adder_fn!(42); |
|
|
|
//! ``` |
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
mod tests { |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn a_bunch_of_print_lns_showing_var_args() { |
|
|
|
println!(); |
|
|
|
println!("{}", 1); |
|
|
|
println!("{} {}", 1, 2); |
|
|
|
println!("{} {} {}", 1, 2, 3); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn shows_vec_macro_and_its_equivalent() { |
|
|
|
let xs: Vec<i32> = vec![1, 2, 3]; |
|
|
|
let ys: Vec<i32> = { |
|
|
|
let mut out = Vec::with_capacity(3); |
|
|
|
out.push(1); |
|
|
|
out.push(2); |
|
|
|
out.push(3); |
|
|
|
out |
|
|
|
}; |
|
|
|
assert_eq!(xs, ys); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn macro_that_if_used_would_generate_illegal_code_is_itself_not_illegal() { |
|
|
|
macro_rules! ah_ah_ah_didnt_say_the_magic_word { |
|
|
|
($x:ident) => { |
|
|
|
let x_mut_1 = &mut $x; |
|
|
|
let x_mut_2 = &mut $x; |
|
|
|
*x_mut_1 += 1i32; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn generic_max_macro_compiles_and_works() { |
|
|
|
macro_rules! max { |
|
|
|
($a:expr, $b:expr) => { |
|
|
|
if $a > $b { $a } else { $b } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
let a: usize = 1; |
|
|
|
let b: usize = 2; |
|
|
|
assert_eq!(max!(a, b), 2); |
|
|
|
|
|
|
|
let c = f64::INFINITY; |
|
|
|
let d = f64::NEG_INFINITY; |
|
|
|
assert_eq!(max!(c, d), f64::INFINITY); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn min_with_partial_ord_will_compile() { |
|
|
|
fn min_that_compiles<T: PartialOrd>(a: T, b: T) -> T { |
|
|
|
if a < b { a } else { b } |
|
|
|
} |
|
|
|
let a: usize = 1; |
|
|
|
let b: usize = 2; |
|
|
|
assert_eq!(min_that_compiles(a, b), 1); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn generated_type_errors_the_example_of_something_that_works() { |
|
|
|
macro_rules! add_one { |
|
|
|
($x:ident) => { |
|
|
|
$x + 1i32 |
|
|
|
} |
|
|
|
} |
|
|
|
let a: i32 = 42; |
|
|
|
|
|
|
|
assert_eq!(add_one!(a), 43); // Ok: expands to i32 + i32 |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn im_curious_if_a_randomly_chosen_example_from_little_quote_unquote_black_book_compiles() { |
|
|
|
macro_rules! abacus { |
|
|
|
((- $($moves:tt)*) -> (+ $($count:tt)*)) => { |
|
|
|
abacus!(($($moves)*) -> ($($count)*)) |
|
|
|
}; |
|
|
|
((- $($moves:tt)*) -> ($($count:tt)*)) => { |
|
|
|
abacus!(($($moves)*) -> (- $($count)*)) |
|
|
|
}; |
|
|
|
((+ $($moves:tt)*) -> (- $($count:tt)*)) => { |
|
|
|
abacus!(($($moves)*) -> ($($count)*)) |
|
|
|
}; |
|
|
|
((+ $($moves:tt)*) -> ($($count:tt)*)) => { |
|
|
|
abacus!(($($moves)*) -> (+ $($count)*)) |
|
|
|
}; |
|
|
|
|
|
|
|
// Check if the final result is zero. |
|
|
|
(() -> ()) => { true }; |
|
|
|
(() -> ($($count:tt)+)) => { false }; |
|
|
|
} |
|
|
|
|
|
|
|
// this one never finishes |
|
|
|
//macro_rules! abacus2 { |
|
|
|
// (-) => {-1}; |
|
|
|
// (+) => {1}; |
|
|
|
// ($($moves:tt)*) => { |
|
|
|
// 0 $(+ abacus2!($moves))* |
|
|
|
// } |
|
|
|
//} |
|
|
|
|
|
|
|
let equals_zero = abacus!((++-+-+++--++---++----+) -> ()); |
|
|
|
assert_eq!(equals_zero, true); |
|
|
|
|
|
|
|
//let equals_zero = abacus2!((++-+-+++--++---++----+) -> ()); |
|
|
|
//assert_eq!(equals_zero, true); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn how_about_this_example_from_little_book() { |
|
|
|
macro_rules! parse_unitary_variants { |
|
|
|
(@as_expr $e:expr) => {$e}; |
|
|
|
(@as_item $($i:item)+) => {$($i)+}; |
|
|
|
|
|
|
|
// Exit rules. |
|
|
|
( |
|
|
|
@collect_unitary_variants ($callback:ident ( $($args:tt)* )), |
|
|
|
($(,)*) -> ($($var_names:ident,)*) |
|
|
|
) => { |
|
|
|
parse_unitary_variants! { |
|
|
|
@as_expr |
|
|
|
$callback!{ $($args)* ($($var_names),*) } |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
( |
|
|
|
@collect_unitary_variants ($callback:ident { $($args:tt)* }), |
|
|
|
($(,)*) -> ($($var_names:ident,)*) |
|
|
|
) => { |
|
|
|
parse_unitary_variants! { |
|
|
|
@as_item |
|
|
|
$callback!{ $($args)* ($($var_names),*) } |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Consume an attribute. |
|
|
|
( |
|
|
|
@collect_unitary_variants $fixed:tt, |
|
|
|
(#[$_attr:meta] $($tail:tt)*) -> ($($var_names:tt)*) |
|
|
|
) => { |
|
|
|
parse_unitary_variants! { |
|
|
|
@collect_unitary_variants $fixed, |
|
|
|
($($tail)*) -> ($($var_names)*) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Handle a variant, optionally with an with initialiser. |
|
|
|
( |
|
|
|
@collect_unitary_variants $fixed:tt, |
|
|
|
($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*) |
|
|
|
) => { |
|
|
|
parse_unitary_variants! { |
|
|
|
@collect_unitary_variants $fixed, |
|
|
|
($($tail)*) -> ($($var_names)* $var,) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Abort on variant with a payload. |
|
|
|
( |
|
|
|
@collect_unitary_variants $fixed:tt, |
|
|
|
($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*) |
|
|
|
) => { |
|
|
|
const _error: () = "cannot parse unitary variants from enum with non-unitary variants"; |
|
|
|
}; |
|
|
|
|
|
|
|
// Entry rule. |
|
|
|
(enum $name:ident {$($body:tt)*} => $callback:ident $arg:tt) => { |
|
|
|
parse_unitary_variants! { |
|
|
|
@collect_unitary_variants |
|
|
|
($callback $arg), ($($body)*,) -> () |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
|
|
|
|
not sure how to invoke this one |
|
|
|
|
|
|
|
let mut variants: Vec<&'static str> = Vec::new(); |
|
|
|
|
|
|
|
macro_rules! get_unitary_variant_str { |
|
|
|
{() ($($var_names),*) } => { $( variants.push(stringify!($variant)); )* } |
|
|
|
} |
|
|
|
|
|
|
|
parse_unitary_variants!( |
|
|
|
enum Abcs { |
|
|
|
A, |
|
|
|
B, |
|
|
|
C, |
|
|
|
D, |
|
|
|
E |
|
|
|
} => get_unitary_variant_str () |
|
|
|
); |
|
|
|
*/ |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn tt_muncher() { |
|
|
|
macro_rules! mixed_rules { |
|
|
|
() => {}; |
|
|
|
(trace $name:ident; $($tail:tt)*) => { |
|
|
|
{ |
|
|
|
println!(concat!(stringify!($name), " = {:?}"), $name); |
|
|
|
mixed_rules!($($tail)*); |
|
|
|
} |
|
|
|
}; |
|
|
|
(trace $name:ident = $init:expr; $($tail:tt)*) => { |
|
|
|
{ |
|
|
|
let $name = $init; |
|
|
|
println!(concat!(stringify!($name), " = {:?}"), $name); |
|
|
|
mixed_rules!($($tail)*); |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
mixed_rules!(trace x = 1;); |
|
|
|
} |
|
|
|
|
|
|
|
use std::fmt::Display; |
|
|
|
|
|
|
|
fn i_need_display_not_debug<T: Display>(x: T) -> String { |
|
|
|
format!("{}", x) |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn debug_display_macro_actually_works() { |
|
|
|
#[derive(Debug)] |
|
|
|
struct Point { |
|
|
|
pub x: f64, |
|
|
|
pub y: f64, |
|
|
|
} |
|
|
|
|
|
|
|
macro_rules! debug_display { |
|
|
|
($t:ident) => { |
|
|
|
impl std::fmt::Display for $t { |
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
|
|
|
write!(f, "{:?}", self) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
debug_display!(Point); // poof! it implements Display |
|
|
|
|
|
|
|
let p = Point { x: 1.234, y: 2.345 }; |
|
|
|
|
|
|
|
i_need_display_not_debug(p); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn example_for_why_you_should_use_fully_qualified_names() { |
|
|
|
struct A; |
|
|
|
|
|
|
|
trait Devious { |
|
|
|
fn abc(&self) -> isize { 42 } |
|
|
|
fn xyz(&self) -> isize { 42 } |
|
|
|
} |
|
|
|
|
|
|
|
impl Devious for A {} |
|
|
|
|
|
|
|
let a = A; |
|
|
|
|
|
|
|
macro_rules! didnt_see_it_coming { |
|
|
|
{ $x:ident ;; } => { |
|
|
|
<A as Devious>::xyz(&$x) |
|
|
|
}; |
|
|
|
|
|
|
|
{ $x:ident <-> } => { |
|
|
|
<A as Devious>::abc(&$x) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
assert_eq!(didnt_see_it_coming!{ a <-> }, 42); |
|
|
|
|
|
|
|
/// muhaha |
|
|
|
fn the_upside_down(a: &A) -> isize { |
|
|
|
/// we have our own 'Devious' down here... |
|
|
|
trait Devious { |
|
|
|
fn abc(&self) -> isize { -42 } |
|
|
|
fn xyz(&self) -> isize { -42 } |
|
|
|
} |
|
|
|
|
|
|
|
impl Devious for A {} |
|
|
|
|
|
|
|
didnt_see_it_coming!{ a ;; } |
|
|
|
} |
|
|
|
|
|
|
|
assert_eq!(the_upside_down(&a), -42); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |