Simple crate: let you select the generics to expand

Less than 120 lines: GitHub - zjp-CN/genericity_select

Quick example:

pub struct V<X, Y> {
    x: X,
    y: Y,
}

#[genericity_select(X = f64 | f32 , Y = f64 | f32)]
impl V<X, Y> {
    pub fn add_x(&self, x: X) -> X { self.x + x }

    pub fn add_y(&self, y: Y) -> Y { self.y + y }
}

Expansion output:

impl V<f64, f64> {
    pub fn add_x(&self, x: f64) -> f64 {
        self.x + x
    }
    pub fn add_y(&self, y: f64) -> f64 {
        self.y + y
    }
}
impl V<f32, f64> {
    pub fn add_x(&self, x: f32) -> f32 {
        self.x + x
    }
    pub fn add_y(&self, y: f64) -> f64 {
        self.y + y
    }
}
impl V<f64, f32> {
    pub fn add_x(&self, x: f64) -> f64 {
        self.x + x
    }
    pub fn add_y(&self, y: f32) -> f32 {
        self.y + y
    }
}
impl V<f32, f32> {
    pub fn add_x(&self, x: f32) -> f32 {
        self.x + x
    }
    pub fn add_y(&self, y: f32) -> f32 {
        self.y + y
    }
}

Token replacement is not a good way to implement this. For example consider:

#[genericity_select(T = Vec<f32> | Vec<f64>)]
impl Thing<T> {
    fn demo() {
        fn assert_sync<T: Sync>() {}
        assert_sync::<T>();
    }
}

Your macro expands this to fn assert_sync<Vec<f32>: Sync>() {} which is invalid syntax.

I think it would be better to produce the following instead.

const _: () = {
    type T = Vec<f32>;
    impl Thing<T> {
        fn demo() {
            fn assert_sync<T: Sync>() {}
            assert_sync::<T>();
        }
    }
};
const _: () = {
    type T = Vec<f64>;
    impl Thing<T> {
        fn demo() {
            fn assert_sync<T: Sync>() {}
            assert_sync::<T>();
        }
    }
};
1 Like