Type dispatch by [proc_macro_attribute]

I want to dispatch function according to types, see the example below:

struct Bytes;

impl Bytes {
    fn get<T>(&mut self) -> T {
        todo!()
    }
    
    fn do_something<T>(&mut self, value: T) {
        todo!()
    }
}

#[type_dispatch(i32, f64, u8)]
pub fn f1<T>(_type_id: TypeId, bytes: &mut Bytes) {
    let value: T = bytes.get();
    bytes.do_something(value);
}

pub fn f2(type_id: TypeId, bytes: &mut Bytes) {
    if type_id == TypeId::of::<i32>() {
        let value: i32 = bytes.get();
        bytes.do_something(value);
    } else if type_id == TypeId::of::<f64> {
        let value: f64 = bytes.get();
        bytes.do_something(value);
    } else if type_id == TypeId::of::<u8>() {
        let value: u8 = bytes.get();
        bytes.do_something(value);
    } else {
        panic!("unknown type_id")
    }
}

by using the proc_macro_attribute type_dispatch , function f1 can be convert to function f2.

All the occurrence of type T will be replaced by the concrete types specified in the arguments of type_dispatch.

Are there any crates that can do something like it? Or how can I write a proc_macro_attribute to do this?

I don't think there is a direct crate for this per se, but you can easily write your own macro (not necessarily procedural) for this:

macro_rules! type_dispatch {(
    #[type_dispatch($($Target:ty),+ $(,)?)]
    $( #[$attr:meta] )*
    $pub:vis
    fn $fname:ident <$T:ident> (
        $typeid:ident : $TypeId:ty $(,
        $($rest:tt)*)?
    ) $(-> $Ret:ty)?
    $body:block
) => (
    $( #[$attr] )*
    $pub
    fn $fname (
        $typeid : $TypeId $(,
        $($rest)*)?
    ) $(-> $Ret)?
    {
        match () {
        $(
            | _case if $typeid == <$TypeId>::of::<$Target>() => {
                type $T = $Target;
                $body
            },
        )*
            | _default => ::std::panic!("unknown type_id"),
        }
    }
)} pub(in crate) use type_dispatch;

Then, if you want attribute-position syntax, you can use:

and write:

#[apply(type_dispatch)]
#[type_dispatch(i32, u8)]
fn foo<T> (
    type_id: ::core::any::TypeId,
    …
)
{
    // … body that depends on `T` …
}

fn main ()
{
    foo(::core::any::TypeId::of::<i32>(), …);
    foo(::core::any::TypeId::of::<u8>(), …);
    foo(::core::any::TypeId::of::<u16>(), …);
}

or, without it:

type_dispatch! {
    #[type_dispatch(i32, u8)]
    fn foo<T> …
}
4 Likes