Для этого есть ящик, byte_strings . Чтобы подвести итог ящика, основная идея состоит в том, чтобы использовать объединение с &'static [u8]
(или &'static str
) членом и &'static CStr
членом:
union transmute {
src: &'static [u8],
dst: &'static ::std::ffi::CStr,
}
Так как создание союзов const
и доступполе const
union также равно const
, чтение dst
фактически является const
mem :: transmute. Поскольку CStr
в настоящее время является просто оболочкой для [c_char]
, значение &[u8]
можно безопасно перевести в &CStr
, однако в будущем представление CStr
, скорее всего, изменится. Вы можете проверить, что &CStr
имеет тот же размер, что и &[u8]
, используя небольшой хак с длинами массивов нулевого размера:
const transmute_is_sound_guard: [(); std::mem::size_of::<&'static [u8]>()]
= [(); std::mem::size_of::<&'static ::std::ffi::CStr>()];
Если они не имеют одинаковый размер, Тип проверки Руст будет жаловаться. Собрав все это вместе, вы можете создать макрос, чтобы сделать const &'static CStr
:
use std::ffi::CStr;
use std::mem::size_of;
macro_rules! unsafe_cstr {
($e: expr) => {{
union Transmute {
src: &'static str,
dst: &'static CStr,
}
const _TRANSMUTE_CHECK: [(); size_of::<&'static str>()]
= [(); size_of::<&'static CStr>()];
const RES: &'static CStr = unsafe {
(Transmute { src: concat!($e, "\0") }).dst
};
RES
}}
}
fn main() {
const C: &'static CStr = unsafe_cstr!("Hello, World!");
println!("{:?}", C)
}
К сожалению, этот макрос по-прежнему небезопасен, поскольку он не проверяет нулевое значениебайтов в срезе &str
, что может быть сделано только с процедурным макросом. Ящик byte_strings содержит такой макрос , а также макросы для конкатенации байтовых строковых литералов и других вспомогательных макросов.