about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPavel Grigorenko <GrigorenkoPV@ya.ru>2024-06-23 22:11:35 +0300
committerPavel Grigorenko <GrigorenkoPV@ya.ru>2024-07-29 20:33:11 +0300
commitec921db289ea87fd4030cb7f8a70f6ba3a31c2c7 (patch)
tree99f1b32f801e7dc8ff87bab0657291c51a38ffee
parent4db3d12e6f395babed53dee1d209a5c8699a5ae6 (diff)
downloadrust-ec921db289ea87fd4030cb7f8a70f6ba3a31c2c7.tar.gz
rust-ec921db289ea87fd4030cb7f8a70f6ba3a31c2c7.zip
impl CloneToUninit for str and CStr
-rw-r--r--library/core/src/clone.rs21
-rw-r--r--library/core/tests/clone.rs40
2 files changed, 61 insertions, 0 deletions
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index 76a89eaaff8..409fd2746f5 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -346,6 +346,27 @@ unsafe impl<T: Copy> CloneToUninit for [T] {
     }
 }
 
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for str {
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: str is just a [u8] with UTF-8 invariant
+        unsafe { self.as_bytes().clone_to_uninit(dst as *mut [u8]) }
+    }
+}
+
+#[unstable(feature = "clone_to_uninit", issue = "126799")]
+unsafe impl CloneToUninit for crate::ffi::CStr {
+    #[cfg_attr(debug_assertions, track_caller)]
+    unsafe fn clone_to_uninit(&self, dst: *mut Self) {
+        // SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants.
+        // And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul).
+        // The pointer metadata properly preserves the length (NUL included).
+        // See: `cstr_metadata_is_length_with_nul` in tests.
+        unsafe { self.to_bytes_with_nul().clone_to_uninit(dst as *mut [u8]) }
+    }
+}
+
 /// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
 /// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
 /// Its responsibility is to provide cleanup on unwind by dropping the values that *are*
diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs
index b7130f16f87..71a328733b7 100644
--- a/library/core/tests/clone.rs
+++ b/library/core/tests/clone.rs
@@ -1,5 +1,7 @@
 use core::clone::CloneToUninit;
+use core::ffi::CStr;
 use core::mem::MaybeUninit;
+use core::ptr;
 
 #[test]
 #[allow(suspicious_double_ref_op)]
@@ -81,3 +83,41 @@ fn test_clone_to_uninit_slice_drops_on_panic() {
     drop(a);
     assert_eq!(COUNTER.load(Relaxed), 0);
 }
+
+#[test]
+fn test_clone_to_uninit_str() {
+    let a = "hello";
+
+    let mut storage: MaybeUninit<[u8; 5]> = MaybeUninit::uninit();
+    unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut str) };
+    assert_eq!(a.as_bytes(), unsafe { storage.assume_init() }.as_slice());
+
+    let mut b: Box<str> = "world".into();
+    assert_eq!(a.len(), b.len());
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<str>(&mut b)) };
+    assert_eq!(a, &*b);
+}
+
+#[test]
+fn test_clone_to_uninit_cstr() {
+    let a = c"hello";
+
+    let mut storage: MaybeUninit<[u8; 6]> = MaybeUninit::uninit();
+    unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut CStr) };
+    assert_eq!(a.to_bytes_with_nul(), unsafe { storage.assume_init() }.as_slice());
+
+    let mut b: Box<CStr> = c"world".into();
+    assert_eq!(a.count_bytes(), b.count_bytes());
+    assert_ne!(a, &*b);
+    unsafe { a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b)) };
+    assert_eq!(a, &*b);
+}
+
+#[test]
+fn cstr_metadata_is_length_with_nul() {
+    let s: &CStr = c"abcdef";
+    let p: *const CStr = ptr::from_ref(s);
+    let bytes: *const [u8] = p as *const [u8];
+    assert_eq!(s.to_bytes_with_nul().len(), bytes.len());
+}