Skip to content

Commit 1be6b13

Browse files
committed
Auto merge of #149079 - zachs18:clone_from_ref, r=Mark-Simulacrum
Add `Box::clone_from_ref` and similar under `feature(clone_from_ref)` Tracking issue: #149075 Accepted ACP: rust-lang/libs-team#483 This PR implements `clone_from_ref` (and `try_*` and `_*in` variants), to get a `Box<T>`, `Arc<T>`, or `Rc<T>` by cloning from a `&T` where `T: CloneToUninit`. The "Implement..." commits replace some ad-hoc conversions with `clone_from_ref` variants, which can be split out to a separate PR if desired. This PR will conflict with #148769 due to usage of `Layout::dangling` (which that PR is renaming to `dangling_ptr`), so they should not be rolled up together, and the one which merges later will need to be amended.
2 parents 99ca3fc + 2eac4db commit 1be6b13

File tree

13 files changed

+368
-104
lines changed

13 files changed

+368
-104
lines changed

library/alloc/src/boxed.rs

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@
184184
#![stable(feature = "rust1", since = "1.0.0")]
185185

186186
use core::borrow::{Borrow, BorrowMut};
187-
#[cfg(not(no_global_oom_handling))]
188187
use core::clone::CloneToUninit;
189188
use core::cmp::Ordering;
190189
use core::error::{self, Error};
@@ -733,6 +732,128 @@ impl<T, A: Allocator> Box<T, A> {
733732
}
734733
}
735734

735+
impl<T: ?Sized + CloneToUninit> Box<T> {
736+
/// Allocates memory on the heap then clones `src` into it.
737+
///
738+
/// This doesn't actually allocate if `src` is zero-sized.
739+
///
740+
/// # Examples
741+
///
742+
/// ```
743+
/// #![feature(clone_from_ref)]
744+
///
745+
/// let hello: Box<str> = Box::clone_from_ref("hello");
746+
/// ```
747+
#[cfg(not(no_global_oom_handling))]
748+
#[unstable(feature = "clone_from_ref", issue = "149075")]
749+
#[must_use]
750+
#[inline]
751+
pub fn clone_from_ref(src: &T) -> Box<T> {
752+
Box::clone_from_ref_in(src, Global)
753+
}
754+
755+
/// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails.
756+
///
757+
/// This doesn't actually allocate if `src` is zero-sized.
758+
///
759+
/// # Examples
760+
///
761+
/// ```
762+
/// #![feature(clone_from_ref)]
763+
/// #![feature(allocator_api)]
764+
///
765+
/// let hello: Box<str> = Box::try_clone_from_ref("hello")?;
766+
/// # Ok::<(), std::alloc::AllocError>(())
767+
/// ```
768+
#[unstable(feature = "clone_from_ref", issue = "149075")]
769+
//#[unstable(feature = "allocator_api", issue = "32838")]
770+
#[must_use]
771+
#[inline]
772+
pub fn try_clone_from_ref(src: &T) -> Result<Box<T>, AllocError> {
773+
Box::try_clone_from_ref_in(src, Global)
774+
}
775+
}
776+
777+
impl<T: ?Sized + CloneToUninit, A: Allocator> Box<T, A> {
778+
/// Allocates memory in the given allocator then clones `src` into it.
779+
///
780+
/// This doesn't actually allocate if `src` is zero-sized.
781+
///
782+
/// # Examples
783+
///
784+
/// ```
785+
/// #![feature(clone_from_ref)]
786+
/// #![feature(allocator_api)]
787+
///
788+
/// use std::alloc::System;
789+
///
790+
/// let hello: Box<str, System> = Box::clone_from_ref_in("hello", System);
791+
/// ```
792+
#[cfg(not(no_global_oom_handling))]
793+
#[unstable(feature = "clone_from_ref", issue = "149075")]
794+
//#[unstable(feature = "allocator_api", issue = "32838")]
795+
#[must_use]
796+
#[inline]
797+
pub fn clone_from_ref_in(src: &T, alloc: A) -> Box<T, A> {
798+
let layout = Layout::for_value::<T>(src);
799+
match Box::try_clone_from_ref_in(src, alloc) {
800+
Ok(bx) => bx,
801+
Err(_) => handle_alloc_error(layout),
802+
}
803+
}
804+
805+
/// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails.
806+
///
807+
/// This doesn't actually allocate if `src` is zero-sized.
808+
///
809+
/// # Examples
810+
///
811+
/// ```
812+
/// #![feature(clone_from_ref)]
813+
/// #![feature(allocator_api)]
814+
///
815+
/// use std::alloc::System;
816+
///
817+
/// let hello: Box<str, System> = Box::try_clone_from_ref_in("hello", System)?;
818+
/// # Ok::<(), std::alloc::AllocError>(())
819+
/// ```
820+
#[unstable(feature = "clone_from_ref", issue = "149075")]
821+
//#[unstable(feature = "allocator_api", issue = "32838")]
822+
#[must_use]
823+
#[inline]
824+
pub fn try_clone_from_ref_in(src: &T, alloc: A) -> Result<Box<T, A>, AllocError> {
825+
struct DeallocDropGuard<'a, A: Allocator>(Layout, &'a A, NonNull<u8>);
826+
impl<'a, A: Allocator> Drop for DeallocDropGuard<'a, A> {
827+
fn drop(&mut self) {
828+
let &mut DeallocDropGuard(layout, alloc, ptr) = self;
829+
// Safety: `ptr` was allocated by `*alloc` with layout `layout`
830+
unsafe {
831+
alloc.deallocate(ptr, layout);
832+
}
833+
}
834+
}
835+
let layout = Layout::for_value::<T>(src);
836+
let (ptr, guard) = if layout.size() == 0 {
837+
(layout.dangling(), None)
838+
} else {
839+
// Safety: layout is non-zero-sized
840+
let ptr = alloc.allocate(layout)?.cast();
841+
(ptr, Some(DeallocDropGuard(layout, &alloc, ptr)))
842+
};
843+
let ptr = ptr.as_ptr();
844+
// Safety: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`,
845+
// and is valid for writes for `size_of_val(src)`.
846+
// If this panics, then `guard` will deallocate for us (if allocation occuured)
847+
unsafe {
848+
<T as CloneToUninit>::clone_to_uninit(src, ptr);
849+
}
850+
// Defuse the deallocate guard
851+
core::mem::forget(guard);
852+
// Safety: We just initialized `*ptr` as a clone of `src`
853+
Ok(unsafe { Box::from_raw_in(ptr.with_metadata_of(src), alloc) })
854+
}
855+
}
856+
736857
impl<T> Box<[T]> {
737858
/// Constructs a new boxed slice with uninitialized contents.
738859
///

library/alloc/src/boxed/convert.rs

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
use core::any::Any;
2-
#[cfg(not(no_global_oom_handling))]
3-
use core::clone::TrivialClone;
42
use core::error::Error;
3+
#[cfg(not(no_global_oom_handling))]
4+
use core::fmt;
55
use core::mem;
66
use core::pin::Pin;
7-
#[cfg(not(no_global_oom_handling))]
8-
use core::{fmt, ptr};
97

108
use crate::alloc::Allocator;
119
#[cfg(not(no_global_oom_handling))]
1210
use crate::borrow::Cow;
1311
use crate::boxed::Box;
1412
#[cfg(not(no_global_oom_handling))]
15-
use crate::raw_vec::RawVec;
16-
#[cfg(not(no_global_oom_handling))]
17-
use crate::str::from_boxed_utf8_unchecked;
18-
#[cfg(not(no_global_oom_handling))]
1913
use crate::string::String;
2014
#[cfg(not(no_global_oom_handling))]
2115
use crate::vec::Vec;
@@ -62,35 +56,6 @@ where
6256
}
6357
}
6458

65-
/// Specialization trait used for `From<&[T]>`.
66-
#[cfg(not(no_global_oom_handling))]
67-
trait BoxFromSlice<T> {
68-
fn from_slice(slice: &[T]) -> Self;
69-
}
70-
71-
#[cfg(not(no_global_oom_handling))]
72-
impl<T: Clone> BoxFromSlice<T> for Box<[T]> {
73-
#[inline]
74-
default fn from_slice(slice: &[T]) -> Self {
75-
slice.to_vec().into_boxed_slice()
76-
}
77-
}
78-
79-
#[cfg(not(no_global_oom_handling))]
80-
impl<T: TrivialClone> BoxFromSlice<T> for Box<[T]> {
81-
#[inline]
82-
fn from_slice(slice: &[T]) -> Self {
83-
let len = slice.len();
84-
let buf = RawVec::with_capacity(len);
85-
// SAFETY: since `T` implements `TrivialClone`, this is sound and
86-
// equivalent to the above.
87-
unsafe {
88-
ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
89-
buf.into_box(slice.len()).assume_init()
90-
}
91-
}
92-
}
93-
9459
#[cfg(not(no_global_oom_handling))]
9560
#[stable(feature = "box_from_slice", since = "1.17.0")]
9661
impl<T: Clone> From<&[T]> for Box<[T]> {
@@ -109,7 +74,7 @@ impl<T: Clone> From<&[T]> for Box<[T]> {
10974
/// ```
11075
#[inline]
11176
fn from(slice: &[T]) -> Box<[T]> {
112-
<Self as BoxFromSlice<T>>::from_slice(slice)
77+
Box::clone_from_ref(slice)
11378
}
11479
}
11580

@@ -170,7 +135,7 @@ impl From<&str> for Box<str> {
170135
/// ```
171136
#[inline]
172137
fn from(s: &str) -> Box<str> {
173-
unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) }
138+
Box::clone_from_ref(s)
174139
}
175140
}
176141

library/alloc/src/ffi/c_str.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,8 +766,7 @@ impl From<&CStr> for Box<CStr> {
766766
/// Converts a `&CStr` into a `Box<CStr>`,
767767
/// by copying the contents into a newly allocated [`Box`].
768768
fn from(s: &CStr) -> Box<CStr> {
769-
let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
770-
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
769+
Box::clone_from_ref(s)
771770
}
772771
}
773772

0 commit comments

Comments
 (0)