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}