有什么方法可以创建 const &'static CStr 吗?
Is there any way to create a const &'static CStr?
我在标准库中没有找到任何关于如何制作 const &'static CStr
的内容。我试图制作自己的宏以将 &'static str
文字转换为 &'static CStr
:
macro_rules! cstr {
($e: expr) => {{
const buffer: &str = concat!($e, "[=10=]");
unsafe {std::ffi::CStr::from_bytes_with_nul_unchecked(buffer.as_bytes())}
}}
}
它有几个问题:
- 如果
expr
包含空字节,它会调用未定义的行为
str::as_bytes
不是 const
,所以 &CStr
不是 const
A CStr
是 borrowed type and, as such, isn't made "on its own". Below the hood, it isn't much more than a reference to a CString
,可以从以下任一创建:
- 借用一个
CString
(显而易见)。原始(源)CString
不得删除,CStr
的生命周期仅在源存在时有效
- 来自一段字节,通过
CStr::from_bytes_with_nul
。 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[=11=]".as_bytes()).unwrap();
println!("{:?}", cstr2);
请注意,这些的生命周期显然再次取决于您用来创建 &CStr
的任何生命周期 - 如其声明中的生命周期参数所示
为后代保留:'static
不是硬性要求
要创建一个 const &'static CStr
,你会很困难,你需要一个用于特定宏的外部 crate (lazy_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[=12=]".as_bytes())
};
}
fn test(input: &'static CStr) {
println!("{:?}", FOO.to_str());
}
fn main() {
test(&FOO);
}
lazy_static
的要点是在定义静态引用时允许函数调用;我们可以利用它来即时构建我们的 CStr
,并且由于它是一个静态引用,因此借用它最多有效 'static
。任务完成。
有一个箱子,byte_strings。总结一下 crate,基本思想是使用一个包含 &'static [u8]
(或 &'static str
)成员和 &'static CStr
成员的联合:
union transmute {
src: &'static [u8],
dst: &'static ::std::ffi::CStr,
}
由于构建联合是 const
并且访问 const
联合的字段也是 const
,因此读取 dst
实际上是 const
mem::transmute.由于 CStr
目前只是 [c_char]
的包装器,因此 &[u8]
可以安全地转换为 &CStr
,但是,在将来,CStr
的表示s 可能会改变。您可以通过对零大小数组的长度使用一些技巧来检查 &CStr
是否与 &[u8]
大小相同:
const transmute_is_sound_guard: [(); std::mem::size_of::<&'static [u8]>()]
= [(); std::mem::size_of::<&'static ::std::ffi::CStr>()];
如果它们的大小不同,Rust 的类型检查器会报错。 综合起来,你可以创建一个宏来制作 const &'static CStr
:
use std::ffi::CStr;
use std::mem::size_of;
macro_rules! unsafe_cstr {
($e: expr) => {{
union Transmute {
src: &'static str,
dst: &'static CStr,
}
const _TRANSMUTE_CHECK: [(); size_of::<&'static str>()]
= [(); size_of::<&'static CStr>()];
const RES: &'static CStr = unsafe {
(Transmute { src: concat!($e, "[=12=]") }).dst
};
RES
}}
}
fn main() {
const C: &'static CStr = unsafe_cstr!("Hello, World!");
println!("{:?}", C)
}
不幸的是,这个宏仍然不安全,因为它不检查 &str
切片中的空字节,这只能通过过程宏来完成。 byte_strings crate contains such a macro,以及用于连接字节字符串文字和其他方便宏的宏。
从 Rust 1.46.0(撰写本文时的当前 beta 工具链)开始,这是可能的,现在 std::mem::transmute
作为 const fn
是稳定的。您还可以使用 const fn
s 检查字符串的内容是否有效(即没有空字节),因为您也可以使用基本的条件表达式和循环。通过 panic!
恐慌在常量上下文中尚不可能,但您可以使用隐式恐慌代码(例如 [][0]
)在编译时引发错误。总而言之,这是一个功能齐全的示例,它仅使用 const fn
s 和声明性宏来允许在常量上下文中创建 &'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'[=10=]' {
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, "[=10=]")) }
}};
}
const VALID: &std::ffi::CStr = cstr!("hello world");
// const INVALID: &std::ffi::CStr = cstr!("hello[=10=]world");
fn main() {
println!("Output: {:?}", VALID);
}
请注意,这确实依赖于 CStr
的实现细节(特别是布局与 [u8]
兼容),因此不应在生产代码中使用。
我在标准库中没有找到任何关于如何制作 const &'static CStr
的内容。我试图制作自己的宏以将 &'static str
文字转换为 &'static CStr
:
macro_rules! cstr {
($e: expr) => {{
const buffer: &str = concat!($e, "[=10=]");
unsafe {std::ffi::CStr::from_bytes_with_nul_unchecked(buffer.as_bytes())}
}}
}
它有几个问题:
- 如果
expr
包含空字节,它会调用未定义的行为 str::as_bytes
不是const
,所以&CStr
不是 const
A CStr
是 borrowed type and, as such, isn't made "on its own". Below the hood, it isn't much more than a reference to a CString
,可以从以下任一创建:
- 借用一个
CString
(显而易见)。原始(源)CString
不得删除,CStr
的生命周期仅在源存在时有效 - 来自一段字节,通过
CStr::from_bytes_with_nul
。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[=11=]".as_bytes()).unwrap();
println!("{:?}", cstr2);
请注意,这些的生命周期显然再次取决于您用来创建 &CStr
的任何生命周期 - 如其声明中的生命周期参数所示
为后代保留:'static
不是硬性要求
要创建一个 const &'static CStr
,你会很困难,你需要一个用于特定宏的外部 crate (lazy_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[=12=]".as_bytes())
};
}
fn test(input: &'static CStr) {
println!("{:?}", FOO.to_str());
}
fn main() {
test(&FOO);
}
lazy_static
的要点是在定义静态引用时允许函数调用;我们可以利用它来即时构建我们的 CStr
,并且由于它是一个静态引用,因此借用它最多有效 'static
。任务完成。
有一个箱子,byte_strings。总结一下 crate,基本思想是使用一个包含 &'static [u8]
(或 &'static str
)成员和 &'static CStr
成员的联合:
union transmute {
src: &'static [u8],
dst: &'static ::std::ffi::CStr,
}
由于构建联合是 const
并且访问 const
联合的字段也是 const
,因此读取 dst
实际上是 const
mem::transmute.由于 CStr
目前只是 [c_char]
的包装器,因此 &[u8]
可以安全地转换为 &CStr
,但是,在将来,CStr
的表示s 可能会改变。您可以通过对零大小数组的长度使用一些技巧来检查 &CStr
是否与 &[u8]
大小相同:
const transmute_is_sound_guard: [(); std::mem::size_of::<&'static [u8]>()]
= [(); std::mem::size_of::<&'static ::std::ffi::CStr>()];
如果它们的大小不同,Rust 的类型检查器会报错。 综合起来,你可以创建一个宏来制作 const &'static CStr
:
use std::ffi::CStr;
use std::mem::size_of;
macro_rules! unsafe_cstr {
($e: expr) => {{
union Transmute {
src: &'static str,
dst: &'static CStr,
}
const _TRANSMUTE_CHECK: [(); size_of::<&'static str>()]
= [(); size_of::<&'static CStr>()];
const RES: &'static CStr = unsafe {
(Transmute { src: concat!($e, "[=12=]") }).dst
};
RES
}}
}
fn main() {
const C: &'static CStr = unsafe_cstr!("Hello, World!");
println!("{:?}", C)
}
不幸的是,这个宏仍然不安全,因为它不检查 &str
切片中的空字节,这只能通过过程宏来完成。 byte_strings crate contains such a macro,以及用于连接字节字符串文字和其他方便宏的宏。
从 Rust 1.46.0(撰写本文时的当前 beta 工具链)开始,这是可能的,现在 std::mem::transmute
作为 const fn
是稳定的。您还可以使用 const fn
s 检查字符串的内容是否有效(即没有空字节),因为您也可以使用基本的条件表达式和循环。通过 panic!
恐慌在常量上下文中尚不可能,但您可以使用隐式恐慌代码(例如 [][0]
)在编译时引发错误。总而言之,这是一个功能齐全的示例,它仅使用 const fn
s 和声明性宏来允许在常量上下文中创建 &'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'[=10=]' {
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, "[=10=]")) }
}};
}
const VALID: &std::ffi::CStr = cstr!("hello world");
// const INVALID: &std::ffi::CStr = cstr!("hello[=10=]world");
fn main() {
println!("Output: {:?}", VALID);
}
请注意,这确实依赖于 CStr
的实现细节(特别是布局与 [u8]
兼容),因此不应在生产代码中使用。