Box::new_from_ref for making a Box<T> from a &T where T: CloneToUninit + ?Sized (and Rc and Arc) (original) (raw)

Proposal

Problem statement

Given a t: &T, where T: CloneToUninit, it would be nice to be able to make a heap-allocated container containing a clone of t, such as Box<T> or Rc<T>. Currently, such conversions are only implemented ad-hoc for specific types, e.g. impl From<&str> for Box<str>, impl<T: Clone> From<&[T]> for Arc<[T]>, etc.

Motivating examples or use cases

Consider a custom DST MyDst. It is possible to write a From<&MyDst> for Box<MyDst> using manual allocation and unsafe code to clone fields into the new allocation, but this manual allocation would not be necessary if the standard library provided this functionality, and the user would only need to write the unsafe code to implement CloneToUninit for MyDst to clone the fields into the new allocation.

Now that CloneToUninit is dyn-compatible, this also applies to dyn MyTrait where trait MyTrait: CloneToUninit.

Additionally, since Rc/Arc's heap layout is not specified, it is only possible to write a From<&MyDst> for Rc<MyDst> that makes a Box<MyDst> and goes through From<Box<T>> for Rc<T>, making an unnecessary intermediate allocation which could be avoided if a new Rc<T> could be cloned directly from a &T

Solution sketch

Add a new function new_from_ref to Box<T>, Rc<T>, Arc<T> where T: ?Sized + CloneToUninit, and try_new_from_ref, new_from_ref_in, and try_new_from_ref_in additionally under feature(allocator_api).

impl<T: ?Sized + CloneToUninit> Box { #[unstable(feature = "new_from_ref")] pub fn new_from_ref(src: &T) -> Box { Box::new_from_ref_in(Global) } #[unstable(feature = "new_from_ref")] #[unstable(feature = "allocator_api")] pub fn try_new_from_ref(src: &T) -> Result<Box, AllocError> { Box::try_new_from_ref_in(Global) } } // same for Arc, Rc

// with feature(allocator_api) impl<T: ?Sized + CloneToUninit, A: Allocator> Box<T, A> { #[unstable(feature = "new_from_ref")] #[unstable(feature = "allocator_api")] pub fn new_from_ref_in(src: &T, alloc: A) -> Box<T, A>; #[unstable(feature = "new_from_ref")] #[unstable(feature = "allocator_api")] pub fn try_new_from_ref_in(src: &T) -> Result<Box<T, A>, AllocError; } // same for Arc<T, A>, Rc<T, A>

(WIP branch (Box and partial Rc only): https://github.com/zachs18/rust/tree/new_from_ref)

Alternate bikeshed names: clone_from_ref, cloned_from_ref, new_cloned.

Alternatives

The existing From<&str> for Box<str> (etc) impls in the stdlib could be replaced by general impl<T: ?Sized + CloneToUninit> From<&T> for Box<T>/Rc<T>/Arc<T> impls. Unfortunately, this would be a breaking change, as downstream crates can currently implement From<&LocalType> for Box<LocalType>/Rc/Arc which a new stdlib impl<T: ?Sized + CloneToUninit> From<&T> for Box<T> would conflict with when LocalType: Clone. Also, it would conflict with the existing impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a>, where E = &(dyn Error + 'a) (since alloc cannot know that core won't add an impl CloneToUninit for dyn Error + '_).

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

Second, if there's a concrete solution: