我没有在标准库中找到有关如何制作const &'static CStr
的任何内容。我试图制作自己的宏以将&'static str
文字转换为&'static CStr
:
macro_rules! cstr {
($e: expr) => {{
const buffer: &str = concat!($e, "\0");
unsafe {std::ffi::CStr::from_bytes_with_nul_unchecked(buffer.as_bytes())}
}}
}
但这有两个问题:
expr
包含空字节,则会调用未定义的行为str::as_bytes
不是const
,因此&CStr
不是const 有什么方法可以创建
const &'static CStr
吗? 最佳答案
从Rust 1.46.0(在撰写本文时为当前beta工具链)开始,现在可以这样做,因为std::mem::transmute
作为const fn
是稳定的。您还可以使用const fn
来检查字符串的内容是否有效(即没有空字节),因为您还可以使用基本的条件表达式和循环。在恒定上下文中尚无法通过panic!
进行 panic ,但是您可以使用隐式 panic 代码(例如[][0]
)在编译时引发错误。总而言之,这是一个功能齐全的示例,仅使用const fn
和声明性宏来允许在恒定上下文中创建&'static CStr
,包括检查内容是否为非法的空字节。
#[allow(unconditional_panic)]
const fn illegal_null_in_string() {
[][0]
}
#[doc(hidden)]
pub const fn validate_cstr_contents(bytes: &[u8]) {
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'\0' {
illegal_null_in_string();
}
i += 1;
}
}
macro_rules! cstr {
( $s:literal ) => {{
$crate::validate_cstr_contents($s.as_bytes());
unsafe { std::mem::transmute::<_, &std::ffi::CStr>(concat!($s, "\0")) }
}};
}
const VALID: &std::ffi::CStr = cstr!("hello world");
// const INVALID: &std::ffi::CStr = cstr!("hello\0world");
fn main() {
println!("Output: {:?}", VALID);
}
请注意,这确实依赖于CStr
的实现细节(特别是布局与[u8]
兼容),因此不应在生产代码中使用它。