Skip to content

Commit 0d5ea93

Browse files
committed
impl CloneToUninit for str and CStr
1 parent c3d7fb3 commit 0d5ea93

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

Diff for: library/core/src/clone.rs

+21
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,27 @@ unsafe impl<T: Copy> CloneToUninit for [T] {
343343
}
344344
}
345345

346+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
347+
unsafe impl CloneToUninit for str {
348+
#[cfg_attr(debug_assertions, track_caller)]
349+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
350+
// SAFETY: str is just a [u8] with UTF-8 invariant
351+
unsafe { self.as_bytes().clone_to_uninit(dst as *mut [u8]) }
352+
}
353+
}
354+
355+
#[unstable(feature = "clone_to_uninit", issue = "126799")]
356+
unsafe impl CloneToUninit for crate::ffi::CStr {
357+
#[cfg_attr(debug_assertions, track_caller)]
358+
unsafe fn clone_to_uninit(&self, dst: *mut Self) {
359+
// SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants.
360+
// And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul).
361+
// The pointer metadata properly preserves the length (NUL included).
362+
// See: `cstr_metadata_is_length_with_nul` in tests.
363+
unsafe { self.to_bytes_with_nul().clone_to_uninit(dst as *mut [u8]) }
364+
}
365+
}
366+
346367
/// Ownership of a collection of values stored in a non-owned `[MaybeUninit<T>]`, some of which
347368
/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation.
348369
/// Its responsibility is to provide cleanup on unwind by dropping the values that *are*

Diff for: library/core/tests/clone.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use core::clone::CloneToUninit;
2+
use core::ffi::CStr;
23
use core::mem::MaybeUninit;
4+
use core::ptr;
35

46
#[test]
57
#[allow(suspicious_double_ref_op)]
@@ -80,3 +82,41 @@ fn test_clone_to_uninit_slice_drops_on_panic() {
8082
drop(a);
8183
assert_eq!(COUNTER.load(Relaxed), 0);
8284
}
85+
86+
#[test]
87+
fn test_clone_to_uninit_str() {
88+
let a = "hello";
89+
90+
let mut storage: MaybeUninit<[u8; 5]> = MaybeUninit::uninit();
91+
unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut str) };
92+
assert_eq!(a.as_bytes(), unsafe { storage.assume_init() }.as_slice());
93+
94+
let mut b: Box<str> = "world".into();
95+
assert_eq!(a.len(), b.len());
96+
assert_ne!(a, &*b);
97+
unsafe { a.clone_to_uninit(ptr::from_mut::<str>(&mut b)) };
98+
assert_eq!(a, &*b);
99+
}
100+
101+
#[test]
102+
fn test_clone_to_uninit_cstr() {
103+
let a = c"hello";
104+
105+
let mut storage: MaybeUninit<[u8; 6]> = MaybeUninit::uninit();
106+
unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut CStr) };
107+
assert_eq!(a.to_bytes_with_nul(), unsafe { storage.assume_init() }.as_slice());
108+
109+
let mut b: Box<CStr> = c"world".into();
110+
assert_eq!(a.count_bytes(), b.count_bytes());
111+
assert_ne!(a, &*b);
112+
unsafe { a.clone_to_uninit(ptr::from_mut::<CStr>(&mut b)) };
113+
assert_eq!(a, &*b);
114+
}
115+
116+
#[test]
117+
fn cstr_metadata_is_length_with_nul() {
118+
let s: &CStr = c"abcdef";
119+
let p: *const CStr = ptr::from_ref(s);
120+
let bytes: *const [u8] = p as *const [u8];
121+
assert_eq!(s.to_bytes_with_nul().len(), bytes.len());
122+
}

0 commit comments

Comments
 (0)