use core::clone::CloneToUninit; use core::ffi::CStr; use core::mem::MaybeUninit; use core::ptr; #[test] #[allow(suspicious_double_ref_op)] fn test_borrowed_clone() { let x = 5; let y: &i32 = &x; let z: &i32 = (&y).clone(); assert_eq!(*z, 5); } #[test] fn test_clone_from() { let a = Box::new(5); let mut b = Box::new(10); b.clone_from(&a); assert_eq!(*b, 5); } #[test] fn test_clone_to_uninit_slice_success() { // Using `String`s to exercise allocation and Drop of the individual elements; // if something is aliased or double-freed, at least Miri will catch that. let a: [String; 3] = ["a", "b", "c"].map(String::from); let mut storage: MaybeUninit<[String; 3]> = MaybeUninit::uninit(); let b: [String; 3] = unsafe { a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [String]); storage.assume_init() }; assert_eq!(a, b); } #[test] #[cfg(panic = "unwind")] fn test_clone_to_uninit_slice_drops_on_panic() { use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering::Relaxed; /// A static counter is OK to use as long as _this one test_ isn't run several times in /// multiple threads. static COUNTER: AtomicUsize = AtomicUsize::new(0); /// Counts how many instances are live, and panics if a fifth one is created struct CountsDropsAndPanics {} impl CountsDropsAndPanics { fn new() -> Self { COUNTER.fetch_add(1, Relaxed); Self {} } } impl Clone for CountsDropsAndPanics { fn clone(&self) -> Self { if COUNTER.load(Relaxed) == 4 { panic!("intentional panic") } else { Self::new() } } } impl Drop for CountsDropsAndPanics { fn drop(&mut self) { COUNTER.fetch_sub(1, Relaxed); } } let a: [CountsDropsAndPanics; 3] = core::array::from_fn(|_| CountsDropsAndPanics::new()); assert_eq!(COUNTER.load(Relaxed), 3); let panic_payload = std::panic::catch_unwind(|| { let mut storage: MaybeUninit<[CountsDropsAndPanics; 3]> = MaybeUninit::uninit(); // This should panic halfway through unsafe { a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [CountsDropsAndPanics]); } }) .unwrap_err(); assert_eq!(panic_payload.downcast().unwrap(), Box::new("intentional panic")); // Check for lack of leak, which is what this test is looking for assert_eq!(COUNTER.load(Relaxed), 3, "leaked during clone!"); // Might as well exercise the rest of the drops 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 = "world".into(); assert_eq!(a.len(), b.len()); assert_ne!(a, &*b); unsafe { a.clone_to_uninit(ptr::from_mut::(&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 = c"world".into(); assert_eq!(a.count_bytes(), b.count_bytes()); assert_ne!(a, &*b); unsafe { a.clone_to_uninit(ptr::from_mut::(&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()); }