rustix/
cstr.rs

1/// A macro for [`CStr`] literals.
2///
3/// This can make passing string literals to rustix APIs more efficient, since
4/// most underlying system calls with string arguments expect NUL-terminated
5/// strings, and passing strings to rustix as `CStr`s means that rustix doesn't
6/// need to copy them into a separate buffer to NUL-terminate them.
7///
8/// [`CStr`]: crate::ffi::CStr
9///
10/// # Examples
11///
12/// ```
13/// # #[cfg(feature = "fs")]
14/// # fn main() -> rustix::io::Result<()> {
15/// use rustix::cstr;
16/// use rustix::fs::{statat, AtFlags, CWD};
17///
18/// let metadata = statat(CWD, cstr!("Cargo.toml"), AtFlags::empty())?;
19/// # Ok(())
20/// # }
21/// # #[cfg(not(feature = "fs"))]
22/// # fn main() {}
23/// ```
24#[allow(unused_macros)]
25#[macro_export]
26macro_rules! cstr {
27    ($str:literal) => {{
28        // Check for NUL manually, to ensure safety.
29        //
30        // In release builds, with strings that don't contain NULs, this
31        // constant-folds away.
32        //
33        // We don't use std's `CStr::from_bytes_with_nul`; as of this writing,
34        // that function isn't defined as `#[inline]` in std and doesn't
35        // constant-fold away.
36        assert!(
37            !$str.bytes().any(|b| b == b'\0'),
38            "cstr argument contains embedded NUL bytes",
39        );
40
41        #[allow(unsafe_code, unused_unsafe)]
42        {
43            // Now that we know the string doesn't have embedded NULs, we can
44            // call `from_bytes_with_nul_unchecked`, which as of this writing
45            // is defined as `#[inline]` and completely optimizes away.
46            //
47            // SAFETY: We have manually checked that the string does not
48            // contain embedded NULs above, and we append or own NUL terminator
49            // here.
50            unsafe {
51                $crate::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes())
52            }
53        }
54    }};
55}
56
57#[test]
58fn test_cstr() {
59    use crate::ffi::CString;
60    use alloc::borrow::ToOwned;
61    assert_eq!(cstr!(""), &*CString::new("").unwrap());
62    assert_eq!(cstr!("").to_owned(), CString::new("").unwrap());
63    assert_eq!(cstr!("hello"), &*CString::new("hello").unwrap());
64    assert_eq!(cstr!("hello").to_owned(), CString::new("hello").unwrap());
65}
66
67#[test]
68#[should_panic]
69fn test_invalid_cstr() {
70    let _ = cstr!("hello\0world");
71}
72
73#[test]
74#[should_panic]
75fn test_invalid_empty_cstr() {
76    let _ = cstr!("\0");
77}