Rust 有没有办法创建const&';静态CStr?

Rust 有没有办法创建const&';静态CStr?,rust,ffi,Rust,Ffi,我在标准库中没有找到任何关于如何生成常量和静态CStr的内容。我试图制作自己的宏,将&'static str文本转换为&'static CStr: macro\u规则!cstr{ ($e:expr)=>{{ 常量缓冲区:&str=concat!($e,“\0”); 不安全的{std::ffi::CStr::from_bytes_,未选中_num_(buffer.as_bytes())} }}

我在标准库中没有找到任何关于如何生成
常量和静态CStr
的内容。我试图制作自己的宏,将
&'static str
文本转换为
&'static CStr

macro\u规则!cstr{
($e:expr)=>{{
常量缓冲区:&str=concat!($e,“\0”);
不安全的{std::ffi::CStr::from_bytes_,未选中_num_(buffer.as_bytes())}
}}                                                                           
}     
它有两个问题:

  • 如果
    expr
    包含空字节,它将调用未定义的行为
  • str::由于u字节
    不是常量,因此
    &CStr
    不是常量
  • CStr
    是一个独立的系统,因此,它不是“独立”生成的。在引擎盖下方,它只不过是对a的引用,可以通过以下任一方式创建:

    • 借用
      CString
      (明显)。不得删除原始(源)
      CString
      ,并且
      CStr
      的生存期仅在源存在时有效
    • 从字节片,通过。
      CStr
      的有效期仅与原始切片的有效期相同(原始切片本身的有效期仅与分配在某处的源数据相同)
    通过
    CString
    创建
    CStr
    非常简单:

    let cstring:CString = CString::new("foobar".as_bytes()).unwrap();
    let cstr:&CStr = cstring.as_c_str();
    println!("{:?}", cstr);
    
    let cstr2:&CStr = CStr::from_bytes_with_nul("foobar\0".as_bytes()).unwrap();
    println!("{:?}", cstr2);
    
    转换现有切片也很简单:

    let cstring:CString = CString::new("foobar".as_bytes()).unwrap();
    let cstr:&CStr = cstring.as_c_str();
    println!("{:?}", cstr);
    
    let cstr2:&CStr = CStr::from_bytes_with_nul("foobar\0".as_bytes()).unwrap();
    println!("{:?}", cstr2);
    
    请注意,它们的生存期显然同样取决于用于创建
    &CStr
    的任何对象的生存期,如声明中的生存期参数所示


    为子孙后代保存
    静态
    不是一项硬性要求

    要创建一个
    常量和'static CStr
    ,您需要一个特定宏的外部板条箱(
    lazy\u static
    ),但这是可行的,如下所示:

    #[macro_use] extern crate lazy_static;
    use std::ffi::CStr;
    
    lazy_static! {
        static ref FOO:&'static CStr = unsafe {
            CStr::from_bytes_with_nul_unchecked("foobar\0".as_bytes())
        };
    }
    
    fn test(input: &'static CStr) {
        println!("{:?}", FOO.to_str());
    }
    
    fn main() {
        test(&FOO);
    }
    

    lazy\u static
    的要点是在定义静态引用时允许函数调用;我们可以利用它动态构建我们的
    CStr
    ,因为它是一个静态引用,所以借用它最多对
    '静态
    有效。任务完成。

    这里有一个板条箱。总而言之,板条箱的基本思想是使用一个带有
    &'static[u8]
    (或
    &'static str
    )成员和
    &'static CStr
    成员的接头:

    联合变形{
    src:&静态[u8],
    dst:&'static::std::ffi::CStr,
    }
    
    由于构造联合体是
    const
    ,访问联合体的字段也是
    const
    ,因此读取
    dst
    实际上是
    const
    mem::transmute。由于
    CStr
    目前只是
    [c_char]
    的包装,因此
    和[u8]
    可以安全地切换到
    &CStr
    ,但是,在未来,
    CStr
    的表示形式可能会发生变化。您可以通过对零大小数组的长度进行一点小改动,检查
    &CStr
    的大小是否与
    和[u8]
    相同:

    const transmute是一个声音卫士:[();std::mem::size\u of::()];
    
    如果它们的尺寸不一样,Rust的类型检查器会抱怨将所有这些结合在一起,您可以创建一个宏来创建一个
    常量和“静态CStr”

    使用std::ffi::CStr;
    使用std::mem::size\u;
    宏规则!不安全的cstr{
    ($e:expr)=>{{
    联合蜕变{
    src:&静态str,
    dst:&静态CStr,
    }
    常量转换检查:[();大小::()];
    const RES:&'static CStr=不安全{
    (Transmute{src:concat!($e,“\0”)}).dst
    };
    皇家经济学会
    }}                                                                           
    }
    fn main(){
    常数C:&'static CStr=unsafe_CStr!(“你好,世界!”);
    println!(“{:?}”,C)
    }
    
    不幸的是,此宏仍然不安全,因为它不检查
    &str
    切片中的空字节,这只能通过过程宏来完成。字节字符串以及用于连接字节字符串文本和其他方便宏的宏。

    从Rust 1.46.0(编写时的当前beta工具链)开始,这是可能的,因为
    std::mem::transmute
    作为
    常量fn
    是稳定的。您还可以使用
    const fn
    s检查字符串的内容是否有效(即没有空字节),因为您还可以使用基本条件表达式和循环。恐慌通过
    恐慌在常量上下文中还不可能,但您可以使用隐式恐慌代码(例如
    [[0]
    )在编译时引发错误。总之,这里有一个功能完整的示例,它只使用
    常量fn
    和声明性宏,允许在常量上下文中创建
    和静态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);
    }
    
    #[允许(无条件恐慌)]
    常量fn非法\u空\u在\u字符串()中{
    [][0]
    }
    #[文件(隐藏)]
    发布常量fn验证内容(字节:&[u8]){
    设muti=0;
    而i{{
    $crate::验证内容($s.as_bytes());
    不安全{std::mem::transmute::(concat!($s,“\0”))}
    }};
    }
    常量有效:&std::ffi::CStr=CStr!(“你好世界”);
    //常量无效:&std::ffi::CStr=CStr!(“你好\0world”);
    fn main(){
    println!(“输出:{:?}”,有效);
    }
    

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

    您是否确实确定需要
    和静态CStr
    ,尤其是
    静态
    部分?那在哪里