有没有办法创建 const &'static CStr?

Vae*_*lus 6 ffi rust

我没有在标准库中找到任何有关如何制作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())}
    }}                                                                           
}     
Run Code Online (Sandbox Code Playgroud)

它有几个问题:

  1. 如果expr包含空字节,则会调用未定义的行为
  2. str::as_bytes不是const,所以&CStr不是 const

ape*_*lla 6

从 Rust 1.46.0(撰写本文时当前的测试版工具链)开始,这是可能的,现在它std::mem::transmute作为const fn. 您还可以使用const fns 检查字符串的内容是否有效(即没有空字节),因为您也可以使用基本的条件表达式和循环。在常量上下文中,恐慌 viapanic!尚不可能,但您可以使用隐式恐慌代码(例如[][0])在编译时引发错误。总而言之,这是一个功能齐全的示例,它只使用const fns 和声明性宏来允许&'static CStr在常量上下文中创建 s,包括检查内容是否存在非法空字节。

#[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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这确实依赖于 的实现细节CStr(特别是布局与 兼容[u8]),因此不应在生产代码中使用。

  • 从 Rust 1.72.0 开始,[`CStr::from_bytes_with_nul` 可以在 `const fn` 中使用](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html# stabled-apis) 因此你可以避免 `transmute`,尽管 `Result::unwrap` 仍然不是 const stable,所以你必须手动恐慌。 (2认同)