/// A macro for [`CStr`] literals. /// /// This can make passing string literals to rustix APIs more efficient, since /// most underlying system calls with string arguments expect NUL-terminated /// strings, and passing strings to rustix as `CStr`s means that rustix doesn't /// need to copy them into a separate buffer to NUL-terminate them. /// /// [`CStr`]: crate::ffi::CStr /// /// # Examples /// /// ```rust,no_run /// # #[cfg(feature = "fs")] /// # fn main() -> rustix::io::Result<()> { /// use rustix::cstr; /// use rustix::fs::{cwd, statat, AtFlags}; /// /// let metadata = statat(cwd(), cstr!("test.txt"), AtFlags::empty())?; /// # Ok(()) /// # } /// # #[cfg(not(feature = "fs"))] /// # fn main() {} /// ``` #[allow(unused_macros)] #[macro_export] macro_rules! cstr { ($str:literal) => {{ // Check for NUL manually, to ensure safety. // // In release builds, with strings that don't contain NULs, this // constant-folds away. // // We don't use std's `CStr::from_bytes_with_nul`; as of this writing, // that function isn't defined as `#[inline]` in std and doesn't // constant-fold away. assert!( !$str.bytes().any(|b| b == b'\0'), "cstr argument contains embedded NUL bytes", ); #[allow(unsafe_code, unused_unsafe)] { // Now that we know the string doesn't have embedded NULs, we can call // `from_bytes_with_nul_unchecked`, which as of this writing is defined // as `#[inline]` and completely optimizes away. // // Safety: We have manually checked that the string does not contain // embedded NULs above, and we append or own NUL terminator here. unsafe { $crate::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes()) } } }}; } #[test] fn test_cstr() { use crate::ffi::CString; use alloc::borrow::ToOwned; assert_eq!(cstr!(""), &*CString::new("").unwrap()); assert_eq!(cstr!("").to_owned(), CString::new("").unwrap()); assert_eq!(cstr!("hello"), &*CString::new("hello").unwrap()); assert_eq!(cstr!("hello").to_owned(), CString::new("hello").unwrap()); } #[test] #[should_panic] fn test_invalid_cstr() { let _ = cstr!("hello\0world"); } #[test] #[should_panic] fn test_invalid_empty_cstr() { let _ = cstr!("\0"); }