U32 -> &'static str during compilation?

In crate A I have:

pub const DB_SCHEMA_VERSION: u32 = 17;

in crate B I have:

static VERSIONS_OF_USED_FORMATS: [(PossibleToDownloadItemType, &'static str); 1] =
    [(PossibleToDownloadItemType::MobilebaseSqlite, "17")];

the problem in duplication, can I somehow convert value of const DB_SCHEMA_VERSION: u32
to &'static str during compilation, in const function?

I tried to write:

const fn to_static_str(v: u32) -> &'static str {
    let mut buf = [0_u8; 10], 
    buf[0] = (v % 10) as u8 + b'0';
...
}

and all works fine,
but I have no idea how can I convert mutable array buf to static allocated?

1 Like

const_format can format primitive types into &'static [str] during const eval

3 Likes

An easy, zero-dependency solution is to use the built-in stringify!() macro, e.g.: Playground

macro_rules! number_and_string {
    ($x:expr) => {
        const NUM: usize = $x;
        const STR: &'static str = stringify!($x);
    }
}
6 Likes

I read code for const_format, and find out missed point:

const fn f() -> [u8; 10] {}
const BUF: &'static [u8; 10] = &f();

the final solution is:

const VERSION: u32 = 147;

const fn number_of_digits(mut n: u32) -> usize {
    let mut count = 0;
    loop {
        count += 1;
        n /= 10;
        if n == 0 {
            break;
        }
    }
    count
}

const fn do_format_num(mut n: u32) -> [u8; 10] {
    let ndigits = number_of_digits(n);
    let mut i = 0;
    let mut buf = [0_u8; 10];
    loop {
        let digit = (n % 10) as u8;
        buf[ndigits - i - 1] = b'0' + digit;
        i += 1;
        n /= 10;
        if n == 0 {
            break;
        }
    }
    buf
}

macro_rules! format_num {
    ($num:expr) => {{
        const NDIGITS: usize = number_of_digits($num);
        const NUM_BUF: &[u8; 10] = &do_format_num($num);
        let p = NUM_BUF as *const [u8; 10] as *const [u8; NDIGITS];
        let bytes: &'static [u8] = unsafe { &*p };
        let string: &'static str = match std::str::from_utf8(bytes) {
            Ok(x) => x,
            Err(_) => panic!("integer buffer not valid utf-8"),
        };
        string
    }};
}

const S2: &'static str = format_num!(VERSION);

That unsafe looks very suspicious; the transmute even more so (and transmuting references/pointers is almost never correct). from_utf8_unchecked() is unwarranted, too — its safe counterpart is also const.


Why don't you just use stringify!()? It's way simpler and safe.

I slitly modified my code, thanks for you suggestions.

And I should note, that I think about unsafe as temporary solution,
when Rust allows &NUB_BUF[0..NDIGITS] in const context, I will gladly remove the use unsafe.

I don't want to change crate A that define DB_SCHEMA_VERSION.
Because of really, it is not this crate A problem, that I need it's u32 constant as &'static str.

In fact I can write this code without unsafe:

const VERSION: u32 = 147;

const fn number_of_digits(mut n: u32) -> usize {
    let mut count = 0;
    loop {
        count += 1;
        n /= 10;
        if n == 0 {
            break;
        }
    }
    count
}

const fn do_format_num(mut n: u32) -> [u8; 10] {
    let ndigits = number_of_digits(n);
    let mut i = 0;
    let mut buf = [0_u8; 10];
    loop {
        let digit = (n % 10) as u8;
        buf[ndigits - i - 1] = b'0' + digit;
        i += 1;
        n /= 10;
        if n == 0 {
            break;
        }
    }
    buf
}

const fn copy<const N: usize>(buf: [u8; 10]) -> [u8; N] {
    assert!(N <= 10);
    let mut i = 0;
    let mut ret = [0_u8; N];
    loop {
        if i < N {
            ret[i] = buf[i];
            i += 1;
        } else {
            break;
        }
    }
    ret
}

macro_rules! format_num {
    ($num:expr) => {{
        const NDIGITS: usize = number_of_digits($num);
        const NUM_BUF: &[u8; NDIGITS] = &copy(do_format_num($num));
        let bytes: &'static [u8] = &*NUM_BUF;
        let string: &'static str = match std::str::from_utf8(bytes) {
            Ok(x) => x,
            Err(_) => panic!("integer buffer not valid utf-8"),
        };
        string
    }};
}

const S2: &'static str = format_num!(VERSION);

fn main() {
    println!("Results: {S2}");
}
2 Likes