我没有在标准库中找到有关如何制作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]兼容),因此不应在生产代码中使用它。

    10-02 02:29