#macro #hexga #no-std #map-on

no-std hexga_map_on

Define the map_on! macro that can be used to impl a lot of trait quickly using macros

11 releases

Uses new Rust 2024

new 0.0.10-beta.12 May 25, 2025
0.0.10-beta.11 May 17, 2025
0.0.10-beta.3 Apr 20, 2025

#1181 in Rust patterns

Download history 84/week @ 2025-04-02 42/week @ 2025-04-09 154/week @ 2025-04-16 26/week @ 2025-04-23 13/week @ 2025-04-30 771/week @ 2025-05-07 246/week @ 2025-05-14

1,068 downloads per month
Used in 11 crates (3 directly)

MIT/Apache

16KB
222 lines

HexGa Map On

Define the map_on! macro that can be used to impl a lot of trait quickly using macros You can have a finer control about what you want to impl :

use hexga_map_on::*;

trait MinusOne
{
    const MINUS_ONE : Self;
}

map_on!
(
    (
        i8, i16, i32, i64, isize,
        f32, f64
    ), 
    ($name:ident) => 
    {
        impl MinusOne for $name
        {
            const MINUS_ONE : Self = -1 as Self;
        }
    }
);

Some variation of map_on exist for different use case :

use hexga_map_on::*;

trait Zero
{
    const ZERO : Self;
}

map_on_number!(
    ($name:ident) => 
    {
        impl Zero for $name
        {
            const ZERO : Self = 0 as Self;
        }
    }
);

Available map_on! macro :

  • map_on!

  • map_on_integer_unsigned! : u8, u16, u32, u64, usize

  • map_on_integer_signed! : i8, i16, i32, i64, isize

  • map_on_integer : (u8, u16, u32, u64, usize) + (i8, i16, i32, i64, isize)

  • map_on_float! : f32, f64

  • map_on_number! : (u8, u16, u32, u64, usize) + (i8, i16, i32, i64, isize) + (f32, f64)

  • map_on_number_and_bool! : (u8, u16, u32, u64, usize) + (i8, i16, i32, i64, isize) + (f32, f64) + (bool)

  • map_on_operator_binary_arithmetic_unit : Add, Sub

  • map_on_operator_binary_arithmetic : (Add, Sub) + (Mul, Div, Rem)

  • map_on_operator_binary_bit : BitOr, BitAnd, Shl, Shr

  • map_on_operator_binary : (Add, Sub) + (Mul, Div, Rem) + (BitOr, BitAnd, Shl, Shr)

  • map_on_operator_assign_arithmetic_unit : AddAssign, SubAssign

  • map_on_operator_assign_arithmetic : (AddAssign, SubAssign) + (MulAssign, DivAssign, RemAssign)

  • map_on_operator_assign_bit : BitOrAssign, BitAndAssign, ShlAssign, ShrAssign

  • map_on_operator_assign : (AddAssign, SubAssign) + (MulAssign, DivAssign, RemAssign) + (BitOrAssign, BitAndAssign, ShlAssign, ShrAssign)

  • map_on_operator_unary_bit : Not

  • map_on_operator_unary_arithmetic_unit : Neg, Abs (Note : Abs should be a user defined trait, currently there isn't such a operator on the standard lib)

  • map_on_operator_unary : (Not) + (Neg, Abs)

More Example

You can also use the map_on! macro to call another macro :

use hexga_map_on::*;

trait One
{
    const ONE : Self;
}

macro_rules! impl_one {
    ($type_name:ty) => {
        impl One for $type_name
        {
            const ONE : Self = 1 as Self;
        }
    };
}

map_on_number!(impl_one);

It is also possible to use nested map_on!*1 macro (only when using macro name, or in const context like implementing a trait) :

use hexga_map_on::*;

pub trait CastInto<T>
{
    /// Might lose some precision.
    /// Same semantics as the [as](https://practice.course.rs/type-conversions/as.html) keyword: `4f32 as u64`
    fn cast_to(self) -> T;
}

// Double recursive macro :)
macro_rules! impl_cast_to 
{ 
    ($itself: ty, $cast_into: ty) => 
    { 
        impl CastInto<$cast_into> for $itself
        {
            fn cast_to(self) -> $cast_into { self as _ }
        }
    }; 

    ($cast_into: ty) => 
    {
        map_on_number!(impl_cast_to,$cast_into);
    }; 
}
// Do 144 trait impl in a few lines :) 
map_on_number!(impl_cast_to);

fn main()
{
    assert_eq!(20.5f32 as i8, 20.5f32.cast_to());
    assert_eq!(4.5 as u32, 4.5.cast_to());
    assert_eq!(4u8 as i64, 4u8.cast_to());
}

Implementing a binary operator is also possible :

use hexga_map_on::*;

#[derive(Debug)]
struct X(pub i32);

map_on_operator_binary!(
    (($trait_name: tt, $fn_name: tt)) => 
    {
        impl std::ops::$trait_name for X
        {
            type Output = X;
            fn $fn_name(self, rhs : Self) -> Self::Output { X(self.0.$fn_name(rhs.0)) }
        }
    }
);

fn main() 
{
    let x =         X(9) + X(3) * X(4) / X(2);
    assert_eq!(x.0,   9  +   3  *   4  /   2 );
}

Limitation

Right now it is impossible to use the map_on! macro in a non const context (like in a function body) with lambda syntax.

macro_rules! print_type {
    ($type_name:ty) => {
        println!("print type from macro name {}", ::std::any::type_name::<$type_name>());
    };
}

fn main() 
{
    // work fine
    map_on!((f32, f64), print_type); 

    // Don't work :/
    map_on!((f32, f64),
        ($type_name:ident) => 
        {
            println!("print type from macro lambda {}", ::std::any::type_name::<$type_name>());
        }
    );
}

The reason is that the lambda macro form will create a temporary macro with the name __map_on_inliner. Because nested lambda map_on! macro call will generate a new macro each time with the same name __map_on_inliner, it will conflict with the previous one. So we need a mecansime to scope the macro name.

Right now the only way to do that that I know is to use a const block, which is not ideal because it limit where the macro can be used : in a const context.

const _: () = {
    // definitions, trait impls, etc…
};

https://internals.rust-lang.org/t/anonymous-modules/15441/2?u=thomas-mewily

The definiton for lambda map_on! macro is the following :

($tokens:tt, $($macro_arms:tt)+) => {
    const _: () = {
        macro_rules! __map_on_inliner {
            $($macro_arms)+
        }

        $crate::map_on!(@expand_tokens $tokens);
    };
};

No runtime deps