Skip to main content

into_owned_trait/
lib.rs

1//! This crate provides the [`IntoOwned`] trait, which converts a
2//! *borrowed form* of a type into its *owned form*.
3//!
4//! # Motivation
5//!
6//! A common pattern in performance-sensitive code is to maintain two parallel
7//! representations of a type: a *borrowed* form that holds references into
8//! existing data (cheap to create and copy), and an *owned* form that carries
9//! its own heap-allocated data (can outlive the source).
10//!
11//! ```rust
12//! # use into_owned_trait::IntoOwned;
13//! // Cheap to push onto a stack during processing — no allocation.
14//! #[derive(Clone, Copy)]
15//! enum EventRef<'a> {
16//!     Started(&'a str),
17//!     Finished(&'a str, u64),
18//! }
19//!
20//! // Stored in the final result — heap-allocated, lifetime-free.
21//! enum Event {
22//!     Started(String),
23//!     Finished(String, u64),
24//! }
25//!
26//! impl<'a> IntoOwned for EventRef<'a> {
27//!     type Owned = Event;
28//!
29//!     fn into_owned(self) -> Event {
30//!         match self {
31//!             EventRef::Started(name) => Event::Started(name.to_owned()),
32//!             EventRef::Finished(name, ts) => Event::Finished(name.to_owned(), ts),
33//!         }
34//!     }
35//! }
36//!
37//! let frame = EventRef::Finished("build", 42);
38//! let owned = frame.into_owned();
39//! ```
40//!
41//! [`IntoOwned`] gives generic code a single, uniform interface for this
42//! conversion, regardless of how complex the borrowed type's internal
43//! structure is.
44//!
45//! # Built-in implementations
46//!
47//! Two implementations are provided out of the box as convenience building
48//! blocks when implementing the trait for your own types:
49//!
50//! - `&T` where `T: `[`ToOwned`] (delegates to [`ToOwned::to_owned`])
51//! - [`Cow<'a, T>`][std::borrow::Cow] where `T: `[`ToOwned`] (delegates to
52//!   [`Cow::into_owned`])
53
54use std::borrow::Cow;
55use std::iter::FusedIterator;
56
57/// Conversion of a borrowed type into its fully owned counterpart.
58///
59/// Implement this trait for a *borrowed form* of a type—one that holds
60/// references into existing data—to produce the corresponding *owned form*
61/// that carries its own data and has no lifetime parameters.
62///
63/// The owned form is often a structurally identical type with references
64/// replaced by their owned equivalents (e.g. `&'a str` → `String`,
65/// `&'a T` → `T::Owned`).
66///
67/// # Example
68///
69/// ```rust
70/// use into_owned_trait::IntoOwned;
71///
72/// struct PersonRef<'a> {
73///     name: &'a str,
74///     age: u32,
75/// }
76///
77/// struct Person {
78///     name: String,
79///     age: u32,
80/// }
81///
82/// impl<'a> IntoOwned for PersonRef<'a> {
83///     type Owned = Person;
84///
85///     fn into_owned(self) -> Person {
86///         Person {
87///             name: self.name.to_owned(),
88///             age: self.age,
89///         }
90///     }
91/// }
92///
93/// let p = PersonRef { name: "Alice", age: 30 };
94/// let owned = p.into_owned();
95/// assert_eq!(owned.name, "Alice");
96/// assert_eq!(owned.age, 30);
97/// ```
98pub trait IntoOwned {
99	/// The fully owned type produced by [`into_owned`](Self::into_owned).
100	type Owned;
101
102	/// Converts `self` into a fully owned value, consuming it.
103	///
104	/// Implementations walk each field and convert references to owned data
105	/// (e.g. calling [`ToOwned::to_owned`] or [`Clone::clone`] as
106	/// appropriate). Fields that already hold owned data should be moved
107	/// without cloning.
108	fn into_owned(self) -> Self::Owned;
109}
110
111impl<T: ToOwned + ?Sized> IntoOwned for &T {
112	type Owned = T::Owned;
113
114	fn into_owned(self) -> Self::Owned {
115		self.to_owned()
116	}
117}
118
119macro_rules! impl_into_owned_for_tuple {
120	($($T:ident),+) => {
121		impl<$($T: IntoOwned),+> IntoOwned for ($($T,)+) {
122			type Owned = ($($T::Owned,)+);
123
124			fn into_owned(self) -> Self::Owned {
125				#[allow(non_snake_case)]
126				let ($($T,)+) = self;
127				($($T.into_owned(),)+)
128			}
129		}
130	};
131}
132
133impl_into_owned_for_tuple!(A);
134impl_into_owned_for_tuple!(A, B);
135impl_into_owned_for_tuple!(A, B, C);
136impl_into_owned_for_tuple!(A, B, C, D);
137impl_into_owned_for_tuple!(A, B, C, D, E);
138impl_into_owned_for_tuple!(A, B, C, D, E, F);
139impl_into_owned_for_tuple!(A, B, C, D, E, F, G);
140impl_into_owned_for_tuple!(A, B, C, D, E, F, G, H);
141impl_into_owned_for_tuple!(A, B, C, D, E, F, G, H, I);
142impl_into_owned_for_tuple!(A, B, C, D, E, F, G, H, I, J);
143impl_into_owned_for_tuple!(A, B, C, D, E, F, G, H, I, J, K);
144impl_into_owned_for_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
145
146impl<'a, T: ToOwned + ?Sized> IntoOwned for Cow<'a, T> {
147	type Owned = T::Owned;
148
149	fn into_owned(self) -> T::Owned {
150		Cow::into_owned(self)
151	}
152}
153
154/// An iterator adapter that converts each item from its borrowed form to its
155/// owned form using [`IntoOwned::into_owned`].
156///
157/// Created by wrapping an iterator whose items implement [`IntoOwned`].
158///
159/// # Example
160///
161/// ```rust
162/// use into_owned_trait::{IntoOwned, MapIntoOwned};
163///
164/// let strings: Vec<String> = MapIntoOwned(["hello", "world"].iter().copied()).collect();
165/// assert_eq!(strings, ["hello", "world"]);
166/// ```
167#[derive(Debug, Clone, Copy)]
168pub struct MapIntoOwned<I>(pub I);
169
170impl<I: Iterator> Iterator for MapIntoOwned<I>
171where
172	I::Item: IntoOwned,
173{
174	type Item = <I::Item as IntoOwned>::Owned;
175
176	fn next(&mut self) -> Option<Self::Item> {
177		self.0.next().map(IntoOwned::into_owned)
178	}
179
180	fn size_hint(&self) -> (usize, Option<usize>) {
181		self.0.size_hint()
182	}
183
184	fn count(self) -> usize {
185		self.0.count()
186	}
187
188	fn last(self) -> Option<Self::Item> {
189		self.0.last().map(IntoOwned::into_owned)
190	}
191
192	fn nth(&mut self, n: usize) -> Option<Self::Item> {
193		self.0.nth(n).map(IntoOwned::into_owned)
194	}
195}
196
197impl<I: DoubleEndedIterator> DoubleEndedIterator for MapIntoOwned<I>
198where
199	I::Item: IntoOwned,
200{
201	fn next_back(&mut self) -> Option<Self::Item> {
202		self.0.next_back().map(IntoOwned::into_owned)
203	}
204
205	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
206		self.0.nth_back(n).map(IntoOwned::into_owned)
207	}
208}
209
210impl<I: ExactSizeIterator> ExactSizeIterator for MapIntoOwned<I>
211where
212	I::Item: IntoOwned,
213{
214	fn len(&self) -> usize {
215		self.0.len()
216	}
217}
218
219impl<I: FusedIterator> FusedIterator for MapIntoOwned<I> where I::Item: IntoOwned {}
220
221/// An iterator adapter that converts the `Ok` variant of each [`Result`] item
222/// using [`IntoOwned::into_owned`], passing `Err` items through unchanged.
223///
224/// Created by wrapping an iterator whose items are `Result<T, E>` where
225/// `T: IntoOwned`.
226///
227/// # Example
228///
229/// ```rust
230/// use into_owned_trait::{IntoOwned, MapOkIntoOwned};
231///
232/// let results: Vec<Result<&str, ()>> = vec![Ok("hello"), Ok("world")];
233/// let owned: Vec<Result<String, ()>> = MapOkIntoOwned(results.into_iter()).collect();
234/// assert_eq!(owned, [Ok("hello".to_owned()), Ok("world".to_owned())]);
235/// ```
236#[derive(Debug, Clone, Copy)]
237pub struct MapOkIntoOwned<I>(pub I);
238
239impl<T: IntoOwned, E, I: Iterator<Item = Result<T, E>>> Iterator for MapOkIntoOwned<I> {
240	type Item = Result<T::Owned, E>;
241
242	fn next(&mut self) -> Option<Self::Item> {
243		self.0.next().map(|r| r.map(IntoOwned::into_owned))
244	}
245
246	fn size_hint(&self) -> (usize, Option<usize>) {
247		self.0.size_hint()
248	}
249
250	fn count(self) -> usize {
251		self.0.count()
252	}
253
254	fn last(self) -> Option<Self::Item> {
255		self.0.last().map(|r| r.map(IntoOwned::into_owned))
256	}
257
258	fn nth(&mut self, n: usize) -> Option<Self::Item> {
259		self.0.nth(n).map(|r| r.map(IntoOwned::into_owned))
260	}
261}
262
263impl<T: IntoOwned, E, I: DoubleEndedIterator<Item = Result<T, E>>> DoubleEndedIterator
264	for MapOkIntoOwned<I>
265{
266	fn next_back(&mut self) -> Option<Self::Item> {
267		self.0.next_back().map(|r| r.map(IntoOwned::into_owned))
268	}
269
270	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
271		self.0.nth_back(n).map(|r| r.map(IntoOwned::into_owned))
272	}
273}
274
275impl<T: IntoOwned, E, I: ExactSizeIterator<Item = Result<T, E>>> ExactSizeIterator
276	for MapOkIntoOwned<I>
277{
278	fn len(&self) -> usize {
279		self.0.len()
280	}
281}
282
283impl<T: IntoOwned, E, I: FusedIterator<Item = Result<T, E>>> FusedIterator for MapOkIntoOwned<I> {}