const-concat
const-concat copied to clipboard
Support constants that are numbers
I would like numbers to work in const_concat.
const S: &str = "abc";
const N: u32 = 123;
const C: &str = const_concat!(S, ", ", N); // "abc, 123"
Proof of concept:
#![feature(const_fn, const_fn_union, existential_type, untagged_unions)]
macro_rules! const_cond {
(default => $e:expr,) => {
$e
};
($cond:expr => $e:expr, $($rest:tt)*) => {
const_if($cond, $e, const_cond!($($rest)*))
};
}
macro_rules! const_to_string {
($e:expr) => {{
const fn const_define() -> This {
$e
}
existential type This: ConstToString;
const VALUE: This = const_define();
const FAKE: This = unsafe { transmute::<usize, This>(1) };
const TYPE: ConstType = <This as ConstToString>::TYPE;
const IS_STR: bool = const_type_eq(TYPE, ConstType::StaticStr);
const SEL_STR: This = const_if(IS_STR, VALUE, FAKE);
const AS_STR: &str = unsafe { transmute::<This, &str>(SEL_STR) };
const IS_U32: bool = const_type_eq(TYPE, ConstType::U32);
const SEL_U32: This = const_if(IS_U32, VALUE, FAKE);
const AS_U32: u32 = unsafe { transmute::<This, u32>(SEL_U32) };
const DIGITS: [u8; 10] = [
b'0' + (AS_U32 / 1_000_000_000 % 10) as u8,
b'0' + (AS_U32 / 100_000_000 % 10) as u8,
b'0' + (AS_U32 / 10_000_000 % 10) as u8,
b'0' + (AS_U32 / 1_000_000 % 10) as u8,
b'0' + (AS_U32 / 100_000 % 10) as u8,
b'0' + (AS_U32 / 10_000 % 10) as u8,
b'0' + (AS_U32 / 1_000 % 10) as u8,
b'0' + (AS_U32 / 100 % 10) as u8,
b'0' + (AS_U32 / 10 % 10) as u8,
b'0' + (AS_U32 % 10) as u8,
];
const NUM_STR: &str = unsafe {
transmute::<&'static [u8], &'static str>(const_cond! {
AS_U32 >= 1_000_000_000 => &DIGITS,
AS_U32 >= 100_000_000 => &[DIGITS[1], DIGITS[2], DIGITS[3], DIGITS[4], DIGITS[5], DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 10_000_000 => &[DIGITS[2], DIGITS[3], DIGITS[4], DIGITS[5], DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 1_000_000 => &[DIGITS[3], DIGITS[4], DIGITS[5], DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 100_000 => &[DIGITS[4], DIGITS[5], DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 10_000 => &[DIGITS[5], DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 1_000 => &[DIGITS[6], DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 100 => &[DIGITS[7], DIGITS[8], DIGITS[9]],
AS_U32 >= 10 => &[DIGITS[8], DIGITS[9]],
default => &[DIGITS[9]],
})
};
const STRING: &str = const_cond! {
IS_STR => AS_STR,
IS_U32 => NUM_STR,
default => "ERROR",
};
STRING
}};
}
enum ConstType {
StaticStr,
U32,
}
const fn const_type_eq(a: ConstType, b: ConstType) -> bool {
a as u8 == b as u8
}
unsafe trait ConstToString: Copy {
const TYPE: ConstType;
}
unsafe impl ConstToString for &'static str {
const TYPE: ConstType = ConstType::StaticStr;
}
unsafe impl ConstToString for u32 {
const TYPE: ConstType = ConstType::U32;
}
const fn const_if<T: Copy>(condition: bool, then: T, otherwise: T) -> T {
[otherwise, then][condition as usize]
}
const unsafe fn transmute<From, To>(from: From) -> To {
#[repr(C)]
struct Pad<From> {
from: From,
zeros: [u8; 16],
}
#[allow(unions_with_drop_fields)]
union Transmute<From, To> {
from: Pad<From>,
to: To,
}
Transmute {
from: Pad {
from,
zeros: [0; 16],
},
}.to
}
fn main() {
const N: u32 = 12345;
const N_TO_STRING: &str = const_to_string!(N);
const S: &str = "&str";
const S_TO_STRING: &str = const_to_string!(S);
println!("{:?}", N_TO_STRING); // "12345"
println!("{:?}", S_TO_STRING); // "&str"
}
@Vurich
I don't think there's any good way to do this, if we stringify every element it'll just turn "Hello, world" into "\"Hello, world\"". The best thing to do is const_concat!(S, ", ", stringify!(N)).
I gave a proof of concept of how this would work in a way that is not "stringify every element." Stringify is not used in the proof of concept.
Sorry, I just skimmed it (I've just got back from holiday and I had a lot to check on). That implementation is great. It's a fun exploitation of const semantics, but unfortunately it's a different fun exploitation of const semantics than this repo was created to show off. I'm not really looking for adding complex features to this repo, it's not supposed to be used as a library.