diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 201bacb28c7ee..6e8a1a84664b9 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -3,7 +3,10 @@ use crate::error::Error;
use crate::ffi::c_char;
use crate::fmt;
use crate::intrinsics;
+use crate::iter::FusedIterator;
+use crate::marker::PhantomData;
use crate::ops;
+use crate::ptr::NonNull;
use crate::slice;
use crate::slice::memchr;
use crate::str;
@@ -595,6 +598,26 @@ impl CStr {
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
}
+ /// Iterates over the bytes in this C string.
+ ///
+ /// The returned iterator will **not** contain the trailing nul terminator
+ /// that this C string has.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(cstr_bytes)]
+ /// use std::ffi::CStr;
+ ///
+ /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+ /// assert!(cstr.bytes().eq(*b"foo"));
+ /// ```
+ #[inline]
+ #[unstable(feature = "cstr_bytes", issue = "112115")]
+ pub fn bytes(&self) -> CStrBytes<'_> {
+ CStrBytes::new(self)
+ }
+
/// Yields a &[str]
slice if the `CStr` contains valid UTF-8.
///
/// If the contents of the `CStr` are valid UTF-8 data, this
@@ -675,3 +698,71 @@ impl AsRef for CStr {
self
}
}
+
+/// An iterator over the bytes of a [`CStr`], without the nul terminator.
+///
+/// This struct is created by the [`bytes`] method on [`CStr`].
+/// See its documentation for more.
+///
+/// [`bytes`]: CStr::bytes
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[unstable(feature = "cstr_bytes", issue = "112115")]
+#[derive(Clone, Debug)]
+pub struct CStrBytes<'a> {
+ // since we know the string is nul-terminated, we only need one pointer
+ ptr: NonNull,
+ phantom: PhantomData<&'a u8>,
+}
+impl<'a> CStrBytes<'a> {
+ #[inline]
+ fn new(s: &'a CStr) -> Self {
+ Self {
+ // SAFETY: Because we have a valid reference to the string, we know
+ // that its pointer is non-null.
+ ptr: unsafe { NonNull::new_unchecked(s.inner.as_ptr() as *const u8 as *mut u8) },
+ phantom: PhantomData,
+ }
+ }
+
+ #[inline]
+ fn is_empty(&self) -> bool {
+ // SAFETY: We uphold that the pointer is always valid to dereference
+ // by starting with a valid C string and then never incrementing beyond
+ // the nul terminator.
+ unsafe { *self.ptr.as_ref() == 0 }
+ }
+}
+
+#[unstable(feature = "cstr_bytes", issue = "112115")]
+impl Iterator for CStrBytes<'_> {
+ type Item = u8;
+
+ #[inline]
+ fn next(&mut self) -> Option {
+ // SAFETY: We only choose a pointer from a valid C string, which must
+ // be non-null and contain at least one value. Since we always stop at
+ // the nul terminator, which is guaranteed to exist, we can assume that
+ // the pointer is non-null and valid. This lets us safely dereference
+ // it and assume that adding 1 will create a new, non-null, valid
+ // pointer.
+ unsafe {
+ intrinsics::assume(!self.ptr.as_ptr().is_null());
+
+ let ret = *self.ptr.as_ref();
+ if ret == 0 {
+ None
+ } else {
+ self.ptr = NonNull::new_unchecked(self.ptr.as_ptr().offset(1));
+ Some(ret)
+ }
+ }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option) {
+ if self.is_empty() { (0, Some(0)) } else { (1, None) }
+ }
+}
+
+#[unstable(feature = "cstr_bytes", issue = "112115")]
+impl FusedIterator for CStrBytes<'_> {}