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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
//! When working with iterators/futures or other hign-generic types sometimes it's useful to assert //! that type of some expression implements some traits or even cast smt to `impl Trait`. //! //! This crate provides macros for both goals — [`assert_bound`] and [`as_opaque`]. //! //! [`assert_bound`]: ./macro.assert_bound.html //! [`as_opaque`]: ./macro.as_opaque.html #![no_std] /// Assert that expression implements trait(s) at compile-time. /// /// ## Examples /// ``` /// use assert_bound::assert_bound; /// use std::fmt::Debug; /// /// assert_bound!(() => Debug + Ord + PartialEq<()>); /// ``` /// /// ``` /// # use assert_bound::assert_bound; /// trait T {} /// impl T for () {} /// /// assert_bound!(() => T); /// ``` /// **Note**: expression is **not** executed: /// ``` /// # use assert_bound::assert_bound; /// let mut var = 0; /// assert_bound!({ var = 1; } => Eq); /// assert_eq!(var, 0); /// ``` /// However you can execute it by calling result of the macro: /// ``` /// # use assert_bound::assert_bound; /// let mut var = 0; /// assert_bound!({ var = 1; } => Eq)(); /// assert_eq!(var, 1); /// ``` /// ```compile_fail /// # use assert_bound::assert_bound; /// // f32/f64 doesn't implement `Eq`, /// // so rustc will fail to compile that. /// assert_bound!(0.1; Eq); /// ``` /// ## Expand /// Following code: /// ``` /// # use assert_bound::assert_bound; /// assert_bound!({ println!("Hewwo?") } => Ord + PartialEq<()>); /// ``` /// expands to something like this: /// ``` /// || { /// fn assert_bound<T>(_: &T) /// where /// T: Ord, /// T: PartialEq<()>, /// {} /// /// let expr = { println!("Hewwo?") }; /// assert_bound(&expr); /// expr /// }; /// ``` #[macro_export] macro_rules! assert_bound { ( $e:expr => // One trait bound, e.g. `std::fmt::Debug`, `PartialEq<()>` $head:ident $( :: $tail:ident )* $( < $param:ty $(, $p:ty)* > )? // Zero or more trait bounds splited by `+` $(+ $head2:ident $( :: $tail2:ident )* $( < $param2:ty $(, $p2:ty)* > )?)* ) => { // Lambda is needed for discarding $e (lambda is returned from macro so // it's possible to call it in order to not discard) || { /// Assert that `T` implements traits these were given to macro #[inline(always)] fn assert_bound<__EXPR_TYPE>(_: &__EXPR_TYPE) where __EXPR_TYPE: $head $( :: $tail )* $( < $param $(, $p)* > )?, $( __EXPR_TYPE: $head2 $( :: $tail2 )* $( < $param2 $(, $p2)* > )? , )* {} let expr = $e; // Assert that $e implement traits assert_bound(&$e); // Return $e from lambda expr } }; } /// Cast type to opaque type that implements trait(s) (`impl Trait + Trait2`) /// /// ``` /// # use assert_bound::as_opaque; /// /// println!("{:?}", as_opaque!(() => std::fmt::Debug)); // `as_opaque!` return `impl Debug` /// ``` /// /// ``` /// # use assert_bound::as_opaque; /// let t = as_opaque!(() => PartialEq<()> + PartialOrd<()>); /// /// assert!(t == ()); /// assert_eq!(t.partial_cmp(&()), Some(std::cmp::Ordering::Equal)); /// ``` /// /// ```compile_fail /// # use assert_bound::{as_opaque, assert_bound}; /// assert_bound!(&(), Eq); // OK /// assert_bound!(&as_opaque!((); Ord), Ord); // OK /// assert_bound!(&as_opaque!((); Ord), Eq); // Error /// ``` /// /// ## Expand /// Following code: /// ``` /// # use assert_bound::as_opaque; /// as_opaque!(() => PartialEq<()> + PartialOrd<()>); /// ``` /// expands to something like this: /// ``` /// fn as_opaque<T>(expr: T) -> impl PartialEq<()> + PartialOrd<()> + 'static /// where /// T: PartialEq<()>, /// T: PartialOrd<()>, /// T: 'static, /// { /// expr /// } /// /// let expr = (); /// as_opaque(expr); /// ``` #[macro_export] macro_rules! as_opaque { ( $e:expr => // One trait bound, e.g. `std::fmt::Debug`, `PartialEq<()>` $head:ident $( :: $tail:ident )* $( < $param:ty $(, $p:ty)* > )? // Zero or more trait bounds splited by `+` $(+ $head2:ident $( :: $tail2:ident )* $( < $param2:ty $(, $p2:ty)* > )?)* // lifetime ; $lifetime:tt ) => { { /// Cast type to anonymous type that implements trait(s) these were given to macro #[inline(always)] fn as_opaque<'lifetime, __EXPR_TYPE>(expr: __EXPR_TYPE) -> impl $head $( :: $tail )* $( < $param $(, $p)* > )? $(+ $head2 $( :: $tail2 )* $( < $param2 $(, $p2)* > )? )* + 'lifetime where __EXPR_TYPE: $head $( :: $tail )* $( < $param $(, $p)* > )?, $( __EXPR_TYPE: $head2 $( :: $tail2 )* $( < $param2 $(, $p2)* > )? , )* __EXPR_TYPE: 'lifetime, { expr } let expr = $e; let opaque = as_opaque::<$lifetime, _>(expr); opaque } }; // Variant without lifetime (use default 'static) ( $e:expr => // One trait bound, e.g. `std::fmt::Debug`, `PartialEq<()>` $head:ident $( :: $tail:ident )* $( < $param:ty $(, $p:ty)* > )? // Zero or more trait bounds splited by `+` $(+ $head2:ident $( :: $tail2:ident )* $( < $param2:ty $(, $p2:ty)* > )?)* ) => { $crate::as_opaque!( $e => $head $( :: $tail )* $( < $param $(, $p)* > )? $(+ $head2 $( :: $tail2 )* $( < $param2 $(, $p2)* > )?)* ; 'static ) }; }