我没有在标准库中找到有关如何制作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
?
答案 0 :(得分:3)
从Rust 1.46.0(撰写本文时为当前Beta版工具链)开始,由于std::mem::transmute
与const fn
一样稳定,因此这是可能的。您还可以使用const fn
来检查字符串的内容是否有效(即没有空字节),因为您也可以使用基本的条件表达式和循环。在恒定上下文中尚无法通过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]
兼容),因此不应在生产代码中使用。
答案 1 :(得分:2)
CStr
是borrowed type,因此不是“单独”制作的。在引擎盖下,它仅是对CString
的引用,并且可以通过以下任一方式创建:
CString
(显而易见)。原始(来源)CString
不得删除,CStr
的生存期仅在来源存在的前提下有效CStr::from_bytes_with_nul
。 CStr
的有效期仅与原始切片的时间相同(其本身仅与在 somewhere 处分配的源数据一样有效)通过CStr
创建CString
很简单:
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
的对象的生存期-如其声明中的lifetime参数所指示
保留后代:'static
并不是硬性要求
要创建一个const &'static CStr
,您会很费力,并且需要一个用于特定宏(lazy_static
)的外部包装箱,但这是可行的,例如: / p>
#[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_static
的意义是在定义静态引用时允许函数调用;我们可以利用它来动态构建我们的CStr
,并且由于它是静态引用,因此借用它最多可适用于'static
。任务完成了。
答案 2 :(得分:0)
对此有一个箱子,byte_strings。总结一下箱子,基本思想是使用具有private fun animateMainView(drawerView: View, slideOffset: Float) {
val scaleFactor = 6f
val slideX = drawerView.width * slideOffset
fragment.view?.translationX = slideX
fragment.view?.scaleX = 1 - (slideOffset / scaleFactor)
fragment.view?.scaleY = 1 - (slideOffset / scaleFactor)
}
(或&'static [u8]
)成员和&'static str
成员的联合:
&'static CStr
由于构造联合是union transmute {
src: &'static [u8],
dst: &'static ::std::ffi::CStr,
}
,并且访问const
联合字段也是const
,因此读const
实际上是dst
mem :: transmute。由于const
目前仅是CStr
的包装,因此可以安全地将[c_char]
截断为&[u8]
,但是将来,&CStr
的表示形式可能会改变。您可以通过使用长度为零大小的数组的小技巧,来检查CStr
与&CStr
的大小是否相同:
&[u8]
如果它们的大小不相同,Rust的类型检查器将抱怨。 将它们放在一起,您可以创建一个宏来制作一个const transmute_is_sound_guard: [(); std::mem::size_of::<&'static [u8]>()]
= [(); std::mem::size_of::<&'static ::std::ffi::CStr>()];
:
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, "\0") }).dst
};
RES
}}
}
fn main() {
const C: &'static CStr = unsafe_cstr!("Hello, World!");
println!("{:?}", C)
}
片中的空字节,这只能通过程序宏来完成。 byte_strings板条箱contains such a macro,以及用于连接字节字符串文字和其他便利宏的宏。