1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! `whiteout` provides macros that erase the type of any value into 
//! an `impl Trait` for a given trait.
//!
//! # Examples
//!
//! Single values can be erased using the `erase!` macro.
//!
//! ```
//!# #[macro_use]
//!# extern crate whiteout;
//!# fn main() {
//! let a = erase!(10, std::ops::Add<i64, Output=i64>);
//! let b = erase!(5, std::ops::Add<i64, Output=i64>);
//! assert_eq!(a + 10, 20);
//! assert_eq!(b + 10, 15);
//! // This fails, the types are opaque
//! // assert_eq!(a + b, 15);
//!# }
//! ```
//!
//! These erased values can't be used together, though; they have different
//! anonymized types. Therefore, you sometimes need the `eraser!` macro.
//!
//! ```
//! #[macro_use]
//! extern crate whiteout;
//!
//! // Define a custom trait into which types will be erased.
//! trait MyTrait: 
//!     std::ops::Add<Self, Output=Self>  // Allow the operation we need
//!     + std::convert::From<i32>  // Allow converting from concrete values
//!     + std::fmt::Debug  // Allow printing (for use with assert!())
//!     + PartialEq  // Allow comparison (for use with assert_eq!())
//!     {}
//!
//! // Implement MyTrait for all possible types.
//! impl<T> MyTrait for T 
//!     where T: std::ops::Add<Self, Output=Self> 
//!     + std::convert::From<i32> 
//!     + std::fmt::Debug
//!     + PartialEq
//!     {}
//!
//! // Create an eraser function for the custom trait
//! eraser!(erase_my_trait, MyTrait);
//!
//! fn main() {
//!     // Use the eraser function. 
//!     // If we used erase!(10, MyTrait); for these
//!     // they would be of different types.
//!     let a = erase_my_trait(10);
//!     let b = erase_my_trait(5);
//!     assert_eq!(a + b, 15.into());
//! }
//! ```
//!
//!


/// `eraser!(name, trait)` creates a function with the given identifier that 
/// erases values to an anonymous type that is `impl Trait` for the given trait.
///
/// # Examples
///
/// ```
/// #[macro_use]
/// extern crate whiteout;
///
/// // Define a custom trait into which types will be erased.
/// trait MyTrait: 
///     std::ops::Add<Self, Output=Self>  // Allow the operation we need
///     + std::convert::From<i32>  // Allow converting from concrete values
///     + std::fmt::Debug  // Allow printing (for use with assert!())
///     + PartialEq  // Allow comparison (for use with assert_eq!())
///     {}
///
/// // Implement MyTrait for all possible types.
/// impl<T> MyTrait for T 
///     where T: std::ops::Add<Self, Output=Self> 
///     + std::convert::From<i32> 
///     + std::fmt::Debug
///     + PartialEq
///     {}
///
/// // Create an eraser function for the custom trait
/// eraser!(erase_my_trait, MyTrait);
///
/// fn main() {
///     // Use the eraser function. 
///     // If we used erase!(10, MyTrait); for these
///     // they would be of different types.
///     let a = erase_my_trait(10);
///     let b = erase_my_trait(5);
///     assert_eq!(a + b, 15.into());
/// }
/// ```
///
///
#[macro_export]
macro_rules! eraser {
    ($name:ident, $($tr:tt)*) => {
            // This function takes any type implementing T and returns impl T
            fn $name<T: $($tr)*>(val: T) -> impl $($tr)* {
                // Do nothing to the value
                val
            }
    }
}


/// `erase!(value, trait)` turns a value of any type that implements trait into 
/// an erasted type which is `impl Trait` for that trait.
/// 
/// # Examples
///
///
/// ```
///# #[macro_use]
///# extern crate whiteout;
///# fn main() {
/// let a = erase!(10, std::ops::Add<i64, Output=i64>);
/// let b = erase!(5, std::ops::Add<i64, Output=i64>);
/// assert_eq!(a + 10, 20);
/// assert_eq!(b + 10, 15);
/// // This fails, the types are opaque
/// // assert_eq!(a + b, 15);
///# }
/// ```
///
#[macro_export]
macro_rules! erase {
    ($val:expr, $($tr:tt)*) => {
        // Creates a block to operate in
        {
            eraser!(f, $($tr)*);
            // Immediately use this function
            f($val)
        }
    }
}