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
H2CO3
April 12, 2023, 5:01pm
3
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);
H2CO3
April 12, 2023, 7:00pm
5
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] = ©(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