clone_into_box/
lib.rs

1//! A library for cloning trait objects.
2//!
3//! ## Instability
4//!
5//! This library depends on an undocumented detail of fat pointer layouts.
6//!
7//! For that reason, this library is intentionally marked as unstable.
8//!
9//! ## Example
10//!
11//! ### Making a cloneable user-defined trait
12//!
13//! ```
14//! use clone_into_box::{CloneIntoBox, CloneIntoBoxExt};
15//!
16//! // Make the trait a subtrait of `CloneIntoBox`
17//! pub trait MyTrait: CloneIntoBox {
18//!     fn hello(&self) -> String;
19//! }
20//!
21//! // Manually implement `Clone` using `clone_into_box`
22//! impl Clone for Box<dyn MyTrait + '_> {
23//!     fn clone(&self) -> Self {
24//!         // Use (**self) to prevent ambiguity.
25//!         // Otherwise you may run into a mysterious stack overflow.
26//!         (**self).clone_into_box()
27//!     }
28//! }
29//!
30//! #[derive(Debug, Clone)]
31//! struct Foo(String);
32//!
33//! impl MyTrait for Foo {
34//!     fn hello(&self) -> String {
35//!         format!("Hello, {}!", self.0)
36//!     }
37//! }
38//!
39//! fn main() {
40//!     let x: Box<dyn MyTrait> = Box::new(Foo(String::from("John")));
41//!     assert_eq!(x.hello(), "Hello, John!");
42//!     let y = x.clone();
43//!     assert_eq!(y.hello(), "Hello, John!");
44//! }
45//! ```
46//!
47//! ### Making a cloneable variant of an existing trait
48//!
49//! ```
50//! use clone_into_box::{CloneIntoBox, CloneIntoBoxExt};
51//!
52//! // Use a "new trait" pattern to create a trait for `ExistingTrait + CloneIntoBox`
53//! pub trait FnClone: Fn() -> String + CloneIntoBox {}
54//! impl<T: Fn() -> String + CloneIntoBox + ?Sized> FnClone for T {}
55//!
56//! // Manually implement `Clone` using `clone_into_box`
57//! impl Clone for Box<dyn FnClone + '_> {
58//!     fn clone(&self) -> Self {
59//!         // Use (**self) to prevent ambiguity.
60//!         // Otherwise you may run into a mysterious stack overflow.
61//!         (**self).clone_into_box()
62//!     }
63//! }
64//!
65//! fn main() {
66//!     let name = String::from("John");
67//!     let x: Box<dyn FnClone> = Box::new(move || format!("Hello, {}!", name));
68//!     assert_eq!(x(), "Hello, John!");
69//!     let y = x.clone();
70//!     assert_eq!(y(), "Hello, John!");
71//! }
72//! ```
73
74// NOTE: this library doesn't explicitly use a library feature which is marked unstable.
75// Nonetheless it's intentionally made unstable because it relies on the internal detail
76// of fat pointer layouts.
77#![feature(rustc_private)]
78
79use std::alloc::{alloc, dealloc, Layout};
80use std::mem::forget;
81use std::ptr::write;
82
83/// A (possibly unsized) value which can be cloned into a pre-allocated space.
84///
85/// Users can use `CloneIntoBoxExt` to clone it into `Box<T>`.
86pub trait CloneIntoBox {
87    /// Clone into the specified place.
88    ///
89    /// ## Effect
90    ///
91    /// After successful invocation, the area pointed to
92    /// by `ptr` will contain a valid representation of `Self`
93    /// with auxiliary data contained in the `self` fat pointer.
94    ///
95    /// ## Safety
96    ///
97    /// The `ptr` parameter must point to an uninitialized area
98    /// which has enough space of `std::mem::size_of_val(self)` bytes
99    /// and is aligned to `std::mem::align_of_val(self)` bytes.
100    ///
101    /// ## Panics
102    ///
103    /// This method isn't expected to panic in normal cases,
104    /// but the caller must handle panics carefully for safety.
105    unsafe fn clone_into_ptr(&self, ptr: *mut u8);
106}
107
108impl<T: Clone> CloneIntoBox for T {
109    unsafe fn clone_into_ptr(&self, ptr: *mut u8) {
110        write(ptr as *mut T, self.clone())
111    }
112}
113
114/// An extension trait for cloning trait objects into `Box`es.
115///
116/// ## Examples
117///
118/// See [crate documentation](index.html) for examples.
119pub trait CloneIntoBoxExt: CloneIntoBox {
120    /// Clone the provided value into a `Box`-allocated space.
121    ///
122    /// ## Examples
123    ///
124    /// See [crate documentation](index.html) for examples.
125    fn clone_into_box(&self) -> Box<Self> {
126        struct Guard {
127            ptr: *mut u8,
128            layout: Layout,
129        }
130        impl Drop for Guard {
131            fn drop(&mut self) {
132                unsafe {
133                    dealloc(self.ptr, self.layout);
134                }
135            }
136        }
137
138        let layout = Layout::for_value::<Self>(self);
139        let ptr = unsafe { alloc(layout) };
140        let guard = Guard { ptr, layout };
141        unsafe {
142            self.clone_into_ptr(ptr);
143        }
144        forget(guard);
145        unsafe { Box::from_raw(assign_thin_mut(self, ptr)) }
146    }
147}
148impl<T: CloneIntoBox + ?Sized> CloneIntoBoxExt for T {}
149
150fn assign_thin_mut<T: ?Sized>(meta: *const T, thin: *mut u8) -> *mut T {
151    let mut fat = meta as *mut T;
152    // Assumes that the first *mut u8 is the thin pointer.
153    unsafe {
154        *(&mut fat as *mut *mut T as *mut *mut u8) = thin;
155    }
156    fat
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    pub trait FnClone: Fn() -> String + CloneIntoBox {}
164    impl<T: ?Sized> FnClone for T where T: Fn() -> String + CloneIntoBox {}
165
166    impl<'a> Clone for Box<dyn FnClone + 'a> {
167        fn clone(&self) -> Self {
168            (**self).clone_into_box()
169        }
170    }
171
172    #[test]
173    fn test_clone_fn() {
174        let s = String::from("Hello,");
175        let f: Box<dyn FnClone> = Box::new(move || format!("{} world!", s));
176        assert_eq!(f(), "Hello, world!");
177        let ff = f.clone();
178        assert_eq!(ff(), "Hello, world!");
179    }
180
181    #[test]
182    #[should_panic(expected = "PanicClone::clone() is called")]
183    fn test_clone_panic() {
184        struct PanicClone;
185        impl Clone for PanicClone {
186            fn clone(&self) -> Self {
187                panic!("PanicClone::clone() is called");
188            }
189        }
190        let s = String::from("Hello,");
191        let p = PanicClone;
192        let f: Box<dyn FnClone> = Box::new(move || {
193            let _ = &p;
194            format!("{} world!", s)
195        });
196        assert_eq!(f(), "Hello, world!");
197        let _ = f.clone();
198    }
199}