//! code examples from "Productive Rust: Implementing Traits with Macros" //! //! # 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(a: T, b: T) -> T { //! if a < b { a } else { b } //! } //! ``` //! //! you can't add an i32 with a HashMap //! //! //! ```compile_fail //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } //! let b: std::collections::HashMap = Default::default(); //! add_one!(b); //! ``` //! //! you can't match a numeric literal against an $ident //! //! //! ```compile_fail //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } //! add_one(42i32); //! ``` //! //! you can't generate a dynamic name with an $expr //! //! //! ```compile_fail //! macro_rules! make_adder_fn { //! ($n:expr) => { //! fn add_$n(rhs: i32) -> i32 { //! $n + rhs //! } //! } //! } //! make_adder_fn!(42); //! ``` //! //! you can't match a $ty to create a struct //! //! //! ```compile_fail //! // ty can't be used to create a type, only to refer to one //! macro_rules! make_struct { //! ($t:ty, $x:ty, $y:ty) => { //! struct $t { //! pub x: f64, //! pub y: f64, //! } //! } //! } //! make_struct!(Point); //! ``` //! //! code order example - with d present, can't compile //! //! ```compile_fail //! fn a() -> i32 { b() } // this is ok, even though `b` is below //! //! const B: i32 = b(); // also ok, even though `b` is below //! //! const fn b() -> i32 { 42 } //! //! d!(); // not ok //! //! macro_rules! d { //! () => { //! fn spooky_numbers() -> [usize; 6] { //! [4, 8, 15, 16, 23, 42] //! } //! } //! } //! ``` #![allow(unused)] pub mod case_study; #[allow(unused)] #[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 = vec![1, 2, 3]; let ys: Vec = { 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_min_macro_compiles_and_works() { macro_rules! min { ($a:expr, $b:expr) => { if $a < $b { $a } else { $b } } } let a: usize = 1; let b: usize = 2; assert_eq!(min!(a, b), 1); let c = f64::INFINITY; let d = f64::NEG_INFINITY; assert_eq!(min!(c, d), f64::NEG_INFINITY); } #[test] fn min_with_partial_ord_will_compile_thats_all_it_needs() { fn min_that_compiles(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 little_book_example_1_does_it_compile() { 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 little_book_example_2_does_it_compile() { 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_does_it_compile() { 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(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); } // did not end up using this #[test] fn example_for_why_you_should_use_fully_qualified_names() { pub struct A; pub 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 ;; } => { ::xyz(&$x) }; { $x:ident <-> } => { ::abc(&$x) } } //let c = ::abc(&a); //macro_rules! you_gotta_be_wiser { // { [[ $x:ident ]] } => { // ::abc(&$x) // } // //{ [[ $x:ident ]] } => { // // ::abc(&$x) // //}; // //{ -^-^- $x:ident -^-^- } => {{ // // // invoking the version that's inside `the_upside_down` // // use crate::tests::example_for_why_you_should_use_fully_qualified_names::the_upside_down::Devious as OtherDevious; // // ::xyz(&$x) // //}}; //} assert_eq!(didnt_see_it_coming!{ a <-> }, 42); //assert_eq!(you_gotta_be_wiser!{ [[ a ]] }, 42); //assert_eq!(you_gotta_be_wiser!{ -^-^- a -^-^- }, -42); /// muhaha fn the_upside_down(a: &A) -> isize { /// we have our own 'Devious' down here... pub 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); } #[test] fn make_adder_with_supplied_name_working_example() { macro_rules! make_adder_fn { ($f:ident, $n:expr) => { fn $f(rhs: i32) -> i32 { $n + rhs } } } make_adder_fn!(add_42, 42); assert_eq!(add_42(0), 42); assert_eq!(add_42(42), 84); } #[test] fn ident_can_be_used_to_create_a_struct() { macro_rules! make_struct { ($t:ident) => { struct $t { pub x: f64, pub y: f64, } } } make_struct!(Point); } #[test] fn std_u32_exmple_works_with_macro_that_has_same_sig_as_uint_impl() { macro_rules! uint_impl_lookalike { ($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, $rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, $reversed:expr, $le_bytes:expr, $be_bytes:expr, $to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { assert!(true); } } uint_impl_lookalike! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } } #[test] fn code_order_can_matter_everything_compiles_except_d() { fn a() -> i32 { b() } // this is ok, even though `b` is below const B: i32 = b(); // also ok, even though `b` is below const fn b() -> i32 { 42 } //d!(); // not ok macro_rules! d { () => { fn spooky_numbers() -> [usize; 6] { [4, 8, 15, 16, 23, 42] } } } } #[test] fn optionally_public_event_attr_can_be_used_from_here() { let active = crate::case_study::Active::default(); let id = active.id(); } }