|
- //! 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<T>(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<String, u32> = 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<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_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<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 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<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);
- }
-
- // 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 ;; } => {
- <A as Devious>::xyz(&$x)
- };
-
- { $x:ident <-> } => {
- <A as Devious>::abc(&$x)
- }
- }
- //let c = <A as ::tests::example_for_why_you_should_use_fully_qualified_names::Devious>::abc(&a);
-
- //macro_rules! you_gotta_be_wiser {
- // { [[ $x:ident ]] } => {
- // <A as ::tests::example_for_why_you_should_use_fully_qualified_names::Devious>::abc(&$x)
- // }
- // //{ [[ $x:ident ]] } => {
- // // <A as crate::tests::example_for_why_you_should_use_fully_qualified_names::Devious>::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;
- // // <A as crate::tests::example_for_why_you_should_use_fully_qualified_names::the_upside_down::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();
- }
-
- }
|