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
,尤其是静态
部分?那在哪里