//! Convenient and efficient string argument passing. //! //! This module defines the `Arg` trait and implements it for several common //! string types. This allows users to pass any of these string types directly //! to rustix APIs with string arguments, and it allows rustix to implement //! NUL-termination without the need for copying where possible.
usecrate::ffi::CStr; usecrate::io; #[cfg(feature = "itoa")] usecrate::path::DecInt; usecrate::path::SMALL_PATH_BUFFER_SIZE; #[cfg(all(feature = "alloc", feature = "itoa"))] use alloc::borrow::ToOwned; use core::mem::MaybeUninit; use core::{ptr, slice, str}; #[cfg(feature = "std")] use std::ffi::{OsStr, OsString}; #[cfg(all(feature = "std", target_os = "hermit"))] use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt}; #[cfg(all(feature = "std", unix))] use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(all(feature = "std", target_os = "vxworks"))] use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt}; #[cfg(all(feature = "std", target_os = "wasi"))] use std::os::wasi::ffi::{OsStrExt, OsStringExt}; #[cfg(feature = "std")] use std::path::{Component, Components, Iter, Path, PathBuf}; #[cfg(feature = "alloc")] use {crate::ffi::CString, alloc::borrow::Cow}; #[cfg(feature = "alloc")] use {alloc::string::String, alloc::vec::Vec};
/// A trait for passing path arguments. /// /// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more /// kinds of strings and can convert into more kinds of strings. /// /// # Examples /// /// ``` /// # #[cfg(any(feature = "fs", feature = "net"))] /// use rustix::ffi::CStr; /// use rustix::io; /// # #[cfg(any(feature = "fs", feature = "net"))] /// use rustix::path::Arg; /// /// # #[cfg(any(feature = "fs", feature = "net"))] /// pub fn touch<P: Arg>(path: P) -> io::Result<()> { /// let path = path.into_c_str()?; /// _touch(&path) /// } /// /// # #[cfg(any(feature = "fs", feature = "net"))] /// fn _touch(path: &CStr) -> io::Result<()> { /// // implementation goes here /// Ok(()) /// } /// ``` /// /// Users can then call `touch("foo")`, `touch(cstr!("foo"))`, /// `touch(Path::new("foo"))`, or many other things. /// /// [`AsRef`]: std::convert::AsRef pubtrait Arg { /// Returns a view of this string as a string slice. fn as_str(&self) -> io::Result<&str>;
/// Returns a potentially-lossy rendering of this string as a /// `Cow<'_, str>`. #[cfg(feature = "alloc")] fn to_string_lossy(&self) -> Cow<'_, str>;
/// Returns a view of this string as a maybe-owned [`CStr`]. #[cfg(feature = "alloc")] fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>;
/// Consumes `self` and returns a view of this string as a maybe-owned /// [`CStr`]. #[cfg(feature = "alloc")] fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> where Self: 'b;
/// Runs a closure with `self` passed in as a `&CStr`. fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> where Self: Sized,
F: FnOnce(&CStr) -> io::Result<T>;
}
/// Runs a closure on `arg` where `A` is mapped to a `&CStr` pubfn option_into_with_c_str<T, F, A: Arg>(arg: Option<A>, f: F) -> io::Result<T> where
A: Sized,
F: FnOnce(Option<&CStr>) -> io::Result<T>,
{ iflet Some(arg) = arg {
arg.into_with_c_str(|p| f(Some(p)))
} else {
f(None)
}
}
/// Runs a closure with `bytes` passed in as a `&CStr`. #[allow(unsafe_code, clippy::int_plus_one)] #[inline] fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T> where
F: FnOnce(&CStr) -> io::Result<T>,
{ // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go // through the dynamic allocation path. If you're opening many files in a // directory with a long path, consider opening the directory and using // `openat` to open the files under it, which will avoid this, and is often // faster in the OS as well.
// Test with >= so that we have room for the trailing NUL. if bytes.len() >= SMALL_PATH_BUFFER_SIZE { return with_c_str_slow_path(bytes, f);
}
// This helps test our safety condition below.
debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE);
// SAFETY: `bytes.len() < SMALL_PATH_BUFFER_SIZE` which means we have space // for `bytes.len() + 1` u8s: unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
buf_ptr.add(bytes.len()).write(0);
}
// SAFETY: We just wrote the bytes above and they will remain valid for the // duration of `f` b/c buf doesn't get dropped until the end of the // function. match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
Ok(s) => f(s),
Err(_) => Err(io::Errno::INVAL),
}
}
/// The slow path which handles any length. In theory OS's only support up to /// `PATH_MAX`, but we let the OS enforce that. #[allow(unsafe_code, clippy::int_plus_one)] #[cold] fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T> where
F: FnOnce(&CStr) -> io::Result<T>,
{ #[cfg(feature = "alloc")]
{
f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?)
}
// This helps test our safety condition below. if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE { return Err(io::Errno::NAMETOOLONG);
}
// SAFETY: `bytes.len() < LARGE_PATH_BUFFER_SIZE` which means we have // space for `bytes.len() + 1` u8s: unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
buf_ptr.add(bytes.len()).write(0);
}
// SAFETY: We just wrote the bytes above and they will remain valid for // the duration of `f` b/c buf doesn't get dropped until the end of the // function. match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) })
{
Ok(s) => f(s),
Err(_) => Err(io::Errno::INVAL),
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.