[−][src]Macro syllogism_macro::impl_specialization
impl_specialization!() { /* proc-macro */ }
Procedural macro to allow IsNot
-based and Specialize
-based specialisation for a number of
data-types, allowing handling each other data-type in the generic way.
It expects a number of items, separated by semicolons (;
):
- a number of data types, possibly with type parameters and lifetimes, each preceded with the
type
keyword (e.g.impl_specialization!(type MyType; type OtherType<T>);
), - optionally a number of traits without type parameters (usually only one trait),
preceded by the
trait
keyword, (e.g.impl_specialization!(trait MyTrait; type MyType; type OtherType<T>);
), - optionally a number of macro names (usually one macro), preceded by the
macro
keyword. These macros are supposed to be macros as defined by thedefine_compatibility
macro in thesyllogism
crate.
This gives you all you need to use IsNot
-based and Specialize
-based implementation, with
the exception of the implementation of Specialize<Self>
for types with type parameters.
In other words, the expansion of the macro contains the following:
- For each pair of distinct data-types
D1
,D2
in the list of data-types supplied, the expansion contains
impl IsNot<D2> for D1 {} // For each pair of distinct data types `D1`, `D2` impl<P> IsNot<P> for D1 where P: T1 {/* ... */ } // For each data type `D1` and each treat `T1` impl Specialize<D2> for D1 { // Return Distinction::Generic } // For each pair of distinct data types `D1`, `D2` impl Specialize<D1> for D1 { // Return Distinction::Special } // For each data type `D1` that has no type parameters impl<P> Specialize<P> for D1 where P: T1 { // Return Distinction::Generic m1!(impl trait for D1 {}); // For each data type `D1` and each macro `m1`
Warning
When using types with type parameters, please note that
- The type parameters must have distinct names. E.g.
impl_specialization(type MyType<T>; type OtherType<U>);
is ok, butimpl_specialization(type MyType<T>; type OtherType<T>);
is not ok. - For the types with type parameters, the macro does not generate an implementation of
Specialize<Self>
. E.g. when you writeimpl_specialization(type MyType<T>; type OtherType);
, you should manually write an impl block forimpl<T> Specialize<MyType<T>> for MyType<T> { /* ... / }
.
Example
use syllogism_macro::impl_specialization; struct S1 {} struct S2<'a, T> { // ... } mod m { pub struct S3<U> { // ... } } macro_rules! my_macro { // ... } trait MyTrait {} impl_specialization!( macro my_macro; trait MyTrait; type S1; type S2<'a, T>; type m::S3<U> );
In this example, the macro invocation expands to something similar to the following
(the difference is that in reality, the expansion contains syllogism::IsNot
and
syllogism::Specialize
, I have simplified this somewhat).
use syllogism::{IsNot, Specialize, Distinction}; impl<'a, T> IsNot<S2<'a, T>> for S1 {} impl<U> IsNot<m::S3<U>> for S1 {} impl<P> IsNot<P> for S1 where P: MyTrait {} impl<'a, T> Specialize<S2<'a, T>> for S1 { fn specialize(self) -> Distinction<S2<'a, T>, S1> { Distinction::Generic(self) } } impl<U> Specialize<m::S3<U>> for S1 { fn specialize(self) -> Distinction<m::S3<U>, S1> { Distinction::Generic(self) } } impl Specialize<S1> for S1 { fn specialize(self) -> Distinction<S1, S1> { Distinction::Special(self) } } impl<P> Specialize<P> for S1 where P: MyTrait { fn specialize(self) -> Distinction<P, S1> { Distinction::Generic(self) } } my_macro!(impl trait for S1 {}); impl<'a, T> IsNot<S1> for S2<'a, T> {} impl<'a, T, U> IsNot<m::S3<U>> for S2<'a, T> {} impl<'a, T, P> IsNot<P> for S2<'a, T> where P: MyTrait {} impl<'a, T> Specialize<S1> for S2<'a, T> { fn specialize(self) -> Distinction<S1, S2<'a, T>> { Distinction::Generic(self) } } impl<'a, T, U> Specialize<m::S3<U>> for S2<'a, T> { fn specialize(self) -> Distinction<m::S3<U>, S2<'a, T>> { Distinction::Generic(self) } } // Note: no `impl<'a, T> Specialize<S2<'a, T>> for S2<'a, T> { /* ... */ }`. impl<'a, P, T> Specialize<P> for S2<'a, T> where P: MyTrait { fn specialize(self) -> Distinction<P, S2<'a, T>> { Distinction::Generic(self) } } my_macro!(impl<'a, T> trait for S2<'a, T> {}); impl<U> IsNot<S1> for m::S3<U> {} impl<'a, U, T> IsNot<S2<'a, T>> for m::S3<U> {} impl<'a, U, P> IsNot<P> for m::S3<U> where P: MyTrait {} impl<U> Specialize<S1> for m::S3<U> { fn specialize(self) -> Distinction<S1, m::S3<U>> { Distinction::Generic(self) } } impl<'a, T, U> Specialize<S2<'a, T>> for m::S3<U> { fn specialize(self) -> Distinction<S2<'a, T>, m::S3<U>> { Distinction::Generic(self) } } // Note: no `impl<U> Specialize<m::S3<U>> for m::S3<U> { /* ... */ }`. impl<'a, P, U> Specialize<P> for m::S3<U> where P: MyTrait { fn specialize(self) -> Distinction<P, m::S3<U>> { Distinction::Generic(self) } } my_macro!(impl<U> trait for m::S3<U> {});
In this way, these types can be used e.g. in the following way:
use syllogism::{IsNot, Specialize, Distinction}; trait GenericTrait<T> { fn generic_method(&mut self, by_value: T); // ... } struct MyStruct { // ... } impl GenericTrait<S1> for MyStruct { // The implementation special for `S1`. } impl<T> GenericTrait<T> for MyStruct where T: syllogism::IsNot<S1> { // The generic implementation. // This can be used for `S2` and `S3` and types defined in other crates // that implement `MyTrait`. } struct MyGenericStruct<T> { // ... } impl<T> GenericTrait<T> for MyGenericStruct<T> where T: Specialize<S1> { fn generic_method(&mut self, by_value: T) { match by_value.specialize() { Distinction::Special(s) => { // `s` has type `S1` and can have a special treatment }, Distinction::Generic(g) => { // The generic implementation. } } } }