Skip to content

Commit 9e497b6

Browse files
committed
Add Box::(try_)clone_from_ref(_in)
1 parent 07bdbae commit 9e497b6

File tree

5 files changed

+130
-8
lines changed

5 files changed

+130
-8
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/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@
374374
// tidy-alphabetical-start
375375
#![feature(alloc_layout_extra)]
376376
#![feature(allocator_api)]
377+
#![feature(clone_from_ref)]
377378
#![feature(get_mut_unchecked)]
378379
#![feature(map_try_insert)]
379380
#![feature(slice_concat_trait)]

tests/ui/privacy/suggest-box-new.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
|
66-
= and 13 other candidates
66+
= and 15 other candidates
6767
help: consider using the `Default` trait
6868
|
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
|
121-
= and 14 other candidates
121+
= and 16 other candidates
122122
help: consider using the `Default` trait
123123
|
124124
LL - let _ = Box {};
@@ -146,7 +146,7 @@ LL + let _ = Box::<i32>::map(_, _);
146146
LL - let _ = Box::<i32> {};
147147
LL + let _ = Box::<i32>::into_inner(_);
148148
|
149-
= and 5 other candidates
149+
= and 7 other candidates
150150
help: consider using the `Default` trait
151151
|
152152
LL - let _ = Box::<i32> {};

tests/ui/suggestions/multi-suggestion.ascii.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
|
66-
= and 13 other candidates
66+
= and 15 other candidates
6767
help: consider using the `Default` trait
6868
|
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
|
121-
= and 14 other candidates
121+
= and 16 other candidates
122122
help: consider using the `Default` trait
123123
|
124124
LL - let _ = Box {};

tests/ui/suggestions/multi-suggestion.unicode.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LL - x: (),
6363
LL - })),
6464
LL + wtf: Some(Box::new_in(_, _)),
6565
66-
╰ and 13 other candidates
66+
╰ and 15 other candidates
6767
help: consider using the `Default` trait
6868
╭╴
6969
LL - wtf: Some(Box(U {
@@ -118,7 +118,7 @@ LL + let _ = Box::new_zeroed();
118118
LL - let _ = Box {};
119119
LL + let _ = Box::new_in(_, _);
120120
121-
╰ and 14 other candidates
121+
╰ and 16 other candidates
122122
help: consider using the `Default` trait
123123
╭╴
124124
LL - let _ = Box {};

0 commit comments

Comments
 (0)