Skip to content

Commit 6210773

Browse files
committed
Add an OOM-handling TryCow type and TryToOwned trait
These are like `std::borrow::Cow` and `std::borrow::ToOwned` but return `OutOfMemory` errors on allocation failure.
1 parent e8a25f5 commit 6210773

File tree

4 files changed

+257
-1
lines changed

4 files changed

+257
-1
lines changed

crates/core/src/alloc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod boxed;
55
mod string;
66
mod try_clone;
77
mod try_collect;
8+
mod try_cow;
89
mod try_new;
910
mod vec;
1011

@@ -16,6 +17,7 @@ pub use boxed::{
1617
pub use string::String;
1718
pub use try_clone::TryClone;
1819
pub use try_collect::{TryCollect, TryExtend, TryFromIterator};
20+
pub use try_cow::{TryCow, TryToOwned};
1921
pub use try_new::{TryNew, try_new};
2022
pub use vec::Vec;
2123

crates/core/src/alloc/string.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
alloc::{TryClone, str_ptr_from_raw_parts, try_realloc},
33
error::OutOfMemory,
44
};
5-
use core::{fmt, mem, ops};
5+
use core::{borrow::Borrow, fmt, mem, ops};
66
use std_alloc::{alloc::Layout, boxed::Box, string as inner};
77

88
/// A newtype wrapper around [`std::string::String`] that only exposes
@@ -48,6 +48,18 @@ impl ops::DerefMut for String {
4848
}
4949
}
5050

51+
impl AsRef<str> for String {
52+
fn as_ref(&self) -> &str {
53+
self
54+
}
55+
}
56+
57+
impl Borrow<str> for String {
58+
fn borrow(&self) -> &str {
59+
self
60+
}
61+
}
62+
5163
impl From<inner::String> for String {
5264
#[inline]
5365
fn from(inner: inner::String) -> Self {

crates/core/src/alloc/try_cow.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
use super::*;
2+
use core::{cmp, fmt, hash, ops::Deref};
3+
use std_alloc::borrow::Borrow;
4+
5+
/// Like [`std::borrow::ToOwned`] but returns an [`OutOfMemory`] error on
6+
/// allocation failure.
7+
pub trait TryToOwned {
8+
/// The owned version of this type.
9+
type Owned: Borrow<Self>;
10+
11+
/// Try to allocate an owned version of `self`.
12+
fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory>;
13+
}
14+
15+
impl TryToOwned for str {
16+
type Owned = String;
17+
18+
fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> {
19+
let mut s = String::new();
20+
s.push_str(self)?;
21+
Ok(s)
22+
}
23+
}
24+
25+
impl<T> TryToOwned for [T]
26+
where
27+
T: TryClone,
28+
{
29+
type Owned = Vec<T>;
30+
31+
fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> {
32+
let mut v = Vec::with_capacity(self.len())?;
33+
for x in self {
34+
v.push(x.try_clone()?)?;
35+
}
36+
Ok(v)
37+
}
38+
}
39+
40+
impl<T> TryToOwned for T
41+
where
42+
T: TryClone,
43+
{
44+
type Owned = Self;
45+
46+
fn try_to_owned(&self) -> Result<Self::Owned, OutOfMemory> {
47+
self.try_clone()
48+
}
49+
}
50+
51+
/// Like [`std::borrow::Cow`] but returns [`OutOfMemory`] errors for various
52+
/// APIs that force allocation of an owned copy.
53+
pub enum TryCow<'a, B>
54+
where
55+
B: 'a + TryToOwned + ?Sized,
56+
{
57+
/// Borrowed data.
58+
Borrowed(&'a B),
59+
60+
/// Owned data.
61+
Owned(<B as TryToOwned>::Owned),
62+
}
63+
64+
impl<'a, B> From<&'a B> for TryCow<'a, B>
65+
where
66+
B: 'a + ?Sized + TryToOwned,
67+
{
68+
fn from(b: &'a B) -> Self {
69+
Self::Borrowed(b)
70+
}
71+
}
72+
73+
impl<B> Default for TryCow<'_, B>
74+
where
75+
B: ?Sized + TryToOwned<Owned: Default>,
76+
{
77+
fn default() -> Self {
78+
Self::Owned(<B as TryToOwned>::Owned::default())
79+
}
80+
}
81+
82+
impl<B> fmt::Debug for TryCow<'_, B>
83+
where
84+
B: ?Sized + fmt::Debug + TryToOwned<Owned: fmt::Debug>,
85+
{
86+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87+
match self {
88+
Self::Borrowed(b) => fmt::Debug::fmt(b, f),
89+
Self::Owned(o) => fmt::Debug::fmt(o, f),
90+
}
91+
}
92+
}
93+
94+
impl<B> TryClone for TryCow<'_, B>
95+
where
96+
B: ?Sized + TryToOwned,
97+
{
98+
fn try_clone(&self) -> Result<Self, OutOfMemory> {
99+
match self {
100+
Self::Borrowed(b) => Ok(Self::Borrowed(b)),
101+
Self::Owned(o) => {
102+
let b: &B = o.borrow();
103+
Ok(Self::Owned(b.try_to_owned()?))
104+
}
105+
}
106+
}
107+
}
108+
109+
impl<B> Deref for TryCow<'_, B>
110+
where
111+
B: ?Sized + TryToOwned,
112+
{
113+
type Target = B;
114+
115+
fn deref(&self) -> &B {
116+
match self {
117+
Self::Borrowed(b) => b,
118+
Self::Owned(o) => o.borrow(),
119+
}
120+
}
121+
}
122+
123+
impl<B> AsRef<B> for TryCow<'_, B>
124+
where
125+
B: ?Sized + TryToOwned,
126+
{
127+
fn as_ref(&self) -> &B {
128+
self
129+
}
130+
}
131+
132+
impl<'a, B> Borrow<B> for TryCow<'a, B>
133+
where
134+
B: ?Sized + TryToOwned,
135+
{
136+
fn borrow(&self) -> &B {
137+
&**self
138+
}
139+
}
140+
141+
impl<B> hash::Hash for TryCow<'_, B>
142+
where
143+
B: ?Sized + hash::Hash + TryToOwned,
144+
{
145+
fn hash<H>(&self, state: &mut H)
146+
where
147+
H: hash::Hasher,
148+
{
149+
hash::Hash::hash(&**self, state)
150+
}
151+
}
152+
153+
impl<B> PartialEq for TryCow<'_, B>
154+
where
155+
B: ?Sized + PartialEq + TryToOwned,
156+
{
157+
fn eq(&self, other: &Self) -> bool {
158+
PartialEq::eq(&**self, &**other)
159+
}
160+
}
161+
162+
impl<B> Eq for TryCow<'_, B> where B: ?Sized + Eq + TryToOwned {}
163+
164+
impl<B> PartialOrd for TryCow<'_, B>
165+
where
166+
B: ?Sized + PartialOrd + TryToOwned,
167+
{
168+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
169+
PartialOrd::partial_cmp(&**self, &**other)
170+
}
171+
}
172+
173+
impl<B> Ord for TryCow<'_, B>
174+
where
175+
B: ?Sized + Ord + TryToOwned,
176+
{
177+
fn cmp(&self, other: &Self) -> cmp::Ordering {
178+
Ord::cmp(&**self, &**other)
179+
}
180+
}
181+
182+
impl<'a, B> TryCow<'a, B>
183+
where
184+
B: TryToOwned + ?Sized,
185+
{
186+
/// Same as [`std::borrow::Cow::to_mut`] but returns an [`OutOfMemory`]
187+
/// error on allocation failure.
188+
pub fn to_mut(&mut self) -> Result<&mut <B as TryToOwned>::Owned, OutOfMemory> {
189+
if let Self::Borrowed(b) = self {
190+
*self = Self::Owned(b.try_to_owned()?);
191+
}
192+
match self {
193+
TryCow::Owned(x) => Ok(x),
194+
TryCow::Borrowed(_) => unreachable!(),
195+
}
196+
}
197+
198+
/// Same as [`std::borrow::Cow::into_owned`] but returns an [`OutOfMemory`]
199+
/// error on allocation failure.
200+
pub fn into_owned(self) -> Result<<B as TryToOwned>::Owned, OutOfMemory> {
201+
match self {
202+
TryCow::Borrowed(b) => b.try_to_owned(),
203+
TryCow::Owned(x) => Ok(x),
204+
}
205+
}
206+
}
207+
208+
#[cfg(test)]
209+
mod tests {
210+
use super::*;
211+
use crate::error::Result;
212+
213+
#[test]
214+
fn to_mut() -> Result<()> {
215+
let mut s = TryCow::Borrowed("hello");
216+
s.to_mut()?.push_str(", world!")?;
217+
assert!(matches!(s, TryCow::Owned(_)));
218+
assert_eq!(&*s, "hello, world!");
219+
Ok(())
220+
}
221+
222+
#[test]
223+
fn into_owned() -> Result<()> {
224+
let v = TryCow::Borrowed(&[42u8, 36][..]);
225+
let v: Vec<u8> = v.into_owned()?;
226+
assert_eq!(&*v, &[42, 36]);
227+
Ok(())
228+
}
229+
}

crates/core/src/alloc/vec.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::alloc::{TryClone, try_realloc};
22
use crate::error::OutOfMemory;
3+
use core::borrow::Borrow;
34
use core::{
45
cmp::Ordering,
56
fmt,
@@ -309,6 +310,18 @@ impl<T> DerefMut for Vec<T> {
309310
}
310311
}
311312

313+
impl<T> AsRef<[T]> for Vec<T> {
314+
fn as_ref(&self) -> &[T] {
315+
self
316+
}
317+
}
318+
319+
impl<T> Borrow<[T]> for Vec<T> {
320+
fn borrow(&self) -> &[T] {
321+
self
322+
}
323+
}
324+
312325
impl<T, I> Index<I> for Vec<T>
313326
where
314327
I: SliceIndex<[T]>,

0 commit comments

Comments
 (0)