You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It is possible to implement a macro that returns a const boolean that indicates if a type implements a certain trait. This might be useful to implement more complex static assertions when used together with static_assertions::const_assert. I took the macro's name from the static_assertions::assert_impl_all macro and then change the assert to is since it takes the same arguments but returns a boolean instead of causing a compiler error.
The macro I came up with looks like this:
macro_rules! is_impl_all {($x:ty: $($t:path),+ $(,)?) => {{struct __Wrapper<T: ?Sized>([*constT; 0]);
// This impl has a trait bound on $x so the type can only be accessed if $x implements $t.impl<T: ?Sized $(+ $t)+> __Wrapper<T> where{
#[allow(non_upper_case_globals)]const __static_assert__is_impl_all: bool = true;
}{// This trait provides an associated const for all types but the inherent implementation's const has a higher priority.trait __Blanket {
#[allow(non_upper_case_globals)]const __static_assert__is_impl_all: bool = false;
}impl<T: ?Sized> __Blanket forT{}// If a blanket trait by the user is in scope that conflicts with the above blanket trait then this will fail to compile but it won't return incorrect values:
__Wrapper::<$x>::__static_assert__is_impl_all
}}};}
Alternative macro implementation
I also made a different version of the macro that ensures that the __static_assert__is_impl_all lookup won't fail if a blanket trait is in scope that conflicts:
macro_rules! is_impl_all {($x:ty: $($t:path),+ $(,)?) => {{pubstruct __Wrapper<T: ?Sized>([*constT; 0]);
// This impl has a trait bound on $x so the type can only be accessed if $x implements $t.impl<T: ?Sized $(+ $t)+> __Wrapper<T> where{
#[allow(non_upper_case_globals)]const __static_assert__is_impl_all: bool = true;
}{// Resolve the type without the inner module being in scope.type __AType = __Wrapper<$x>;
{// This module is private to this scope and won't affect path resolution for the paths provided by the user.mod inner {// Used to reference the type in this new module.pubtraitGetType{typeType;
}// This trait provides an associated const for all types but the inherent implementation's const has a higher priority.trait __Blanket {
#[allow(non_upper_case_globals)]const __static_assert__is_impl_all: bool = false;
}impl<T: ?Sized> __Blanket forT{}pubconstVALUE: bool = <()asGetType>::Type::__static_assert__is_impl_all;
}impl inner::GetTypefor(){typeType = __AType;
}
inner::VALUE}}}};}
Pro: The constants can't have name collision, but the types and traits might still have them.
Con: Probably won't work with type parameters while the previous version should.
Since the __Wrapper struct and the __AType type alias can still cause name conflicts with the paths provided to the macro it doesn't really help that constants name can't cause name conflicts so this version of the macro isn't really better then the previous one and its definitely longer and more complex so it should probably not be used.
Some Tests
Here is some tests I used to see if my macro worked:
#[test]fnit_works(){macro_rules! as_const {($value:expr) => {{constVALUE: bool = {
$value
};
VALUE}};}// Test macro:assert_eq!(as_const!(is_impl_all!((): Send, Sync)), true);assert_eq!(as_const!(is_impl_all!((): Send, From<u8>)), false);assert_eq!(as_const!(is_impl_all!((): From<u8>, From<u16>, Send)), false);// Boolean logic:assert_eq!(as_const!({
is_impl_all!((): Send, std::panic::UnwindSafe)
||
is_impl_all!((): Sync, std::panic::RefUnwindSafe)}), true);// Counting implemented traits:assert_eq!(as_const!({letmut count = 0;
count += is_impl_all!((): Send)asusize;
count += is_impl_all!((): Sync)asusize;
count += is_impl_all!((): From<u8>)asusize;
count += is_impl_all!((): Into<u8>)asusize;
count > 2}), false);assert_eq!(as_const!({letmut count = 0;
count += is_impl_all!((): Send)asusize;
count += is_impl_all!((): Sync)asusize;
count += is_impl_all!((): From<u8>)asusize;
count += is_impl_all!((): Into<u8>)asusize;
count >= 2}), true);}/// The macro isn't perfect and a trait with the correct method name can interfere with the macro./// /// Currently this only causes the macro to not compile and not to return an incorrect value./// /// ```compile_fail/// use playground::is_impl_all;/// /// trait Interfere {/// #[allow(non_upper_case_globals)]/// const __static_assert__is_impl_all: bool = true;/// }/// impl<T> Interfere for T {}////// // These should return false but the above trait might interferes and causes them to return true./// is_impl_all!((): From<u8>);/// is_impl_all!((): From<u8>, From<u16>);/// ```#[allow(dead_code)]fninterference_with_macro(){}
The text was updated successfully, but these errors were encountered:
It is possible to implement a macro that returns a const boolean that indicates if a type implements a certain trait. This might be useful to implement more complex static assertions when used together with
static_assertions::const_assert
. I took the macro's name from thestatic_assertions::assert_impl_all
macro and then change theassert
tois
since it takes the same arguments but returns a boolean instead of causing a compiler error.The macro I came up with looks like this:
Alternative macro implementation
I also made a different version of the macro that ensures that the
__static_assert__is_impl_all
lookup won't fail if a blanket trait is in scope that conflicts:Since the
__Wrapper
struct and the__AType
type alias can still cause name conflicts with the paths provided to the macro it doesn't really help that constants name can't cause name conflicts so this version of the macro isn't really better then the previous one and its definitely longer and more complex so it should probably not be used.Some Tests
Here is some tests I used to see if my macro worked:
The text was updated successfully, but these errors were encountered: