string/lib.rs
1#![deny(warnings, missing_docs, missing_debug_implementations, clippy::all)]
2#![doc(html_root_url = "https://docs.rs/string/0.3.1")]
3
4//! A UTF-8 encoded string with configurable byte storage.
5//!
6//! This crate provides `String`, a type similar to its std counterpart, but
7//! with one significant difference: the underlying byte storage is
8//! configurable. In other words, `String<T>` is a marker type wrapping `T`,
9//! indicating that it represents a UTF-8 encoded string.
10//!
11//! For example, one can represent small strings (stack allocated) by wrapping
12//! an array:
13//!
14//! ```
15//! # use string::*;
16//! let s: String<[u8; 2]> = String::try_from([b'h', b'i']).unwrap();
17//! assert_eq!(&s[..], "hi");
18//! ```
19
20use std::{borrow, default::Default, fmt, hash, ops, str};
21
22/// A UTF-8 encoded string with configurable byte storage.
23///
24/// This type differs from `std::String` in that it is generic over the
25/// underlying byte storage, enabling it to use `Vec<[u8]>`, `&[u8]`, or third
26/// party types, such as [`Bytes`].
27///
28/// In order to construct `String` via any of the non-unsafe constructors,
29/// the backing storage needs to implement the `StableAsRef` marker trait.
30/// If you wish to construct `String` with a type that does not implement `StableAsRef`,
31/// you can use the `from_utf8_unchecked` constructor.
32///
33/// [`Bytes`]: https://docs.rs/bytes/0.4.8/bytes/struct.Bytes.html
34#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
35pub struct String<T = Vec<u8>> {
36 value: T,
37}
38
39impl<T> String<T> {
40 /// Get a reference to the underlying byte storage.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// # use string::*;
46 /// let s = String::new();
47 /// let vec = s.get_ref();
48 /// ```
49 pub fn get_ref(&self) -> &T {
50 &self.value
51 }
52
53 /// Get a mutable reference to the underlying byte storage.
54 ///
55 /// # Safety
56 ///
57 /// It is inadvisable to directly manipulate the byte storage. This function
58 /// is unsafe as the bytes could no longer be valid UTF-8 after mutation.
59 ///
60 /// # Examples
61 ///
62 /// ```
63 /// # use string::*;
64 /// let mut s = String::new();
65 ///
66 /// unsafe {
67 /// let vec = s.get_mut();
68 /// }
69 /// ```
70 pub unsafe fn get_mut(&mut self) -> &mut T {
71 &mut self.value
72 }
73
74 /// Unwraps this `String`, returning the underlying byte storage.
75 ///
76 /// # Examples
77 ///
78 /// ```
79 /// # use string::*;
80 /// let s = String::new();
81 /// let vec = s.into_inner();
82 /// ```
83 pub fn into_inner(self) -> T {
84 self.value
85 }
86
87 /// Creates a new `String` from a &str.
88 ///
89 /// Use `TryFrom` for conversion from &[u8].
90 ///
91 /// ```
92 /// # use string::*;
93 /// let _: String<Vec<u8>> = String::from_str("nice str");
94 /// ```
95 #[allow(clippy::should_implement_trait)]
96 pub fn from_str<'a>(src: &'a str) -> String<T>
97 where
98 T: From<&'a [u8]> + StableAsRef,
99 {
100 let value: T = src.as_bytes().into();
101 Self { value }
102 }
103}
104
105impl String {
106 /// Creates a new empty `String`.
107 ///
108 /// Given that the `String` is empty, this will not allocate.
109 ///
110 /// # Examples
111 ///
112 /// Basic usage
113 ///
114 /// ```
115 /// let s = String::new();
116 /// assert_eq!(s, "");
117 /// ```
118 pub fn new() -> String {
119 String::default()
120 }
121}
122
123impl<T> String<T>
124where
125 T: AsRef<[u8]>,
126{
127 /// Converts the provided value to a `String` without checking that the
128 /// given value is valid UTF-8.
129 ///
130 /// Use `TryFrom` for a safe conversion.
131 ///
132 /// # Safety
133 ///
134 /// You must ensure that:
135 ///
136 /// 1. The backing storage type `T` adheres to the contract as documented on the `StableAsRef`
137 /// marker trait.
138 /// 2. If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the byte slice returned
139 /// by calling `as_ref` and/or `as_mut` on the provided value represents valid utf-8.
140 pub unsafe fn from_utf8_unchecked(value: T) -> String<T> {
141 String { value }
142 }
143}
144
145impl<T> PartialEq<str> for String<T>
146where
147 T: AsRef<[u8]>,
148{
149 fn eq(&self, other: &str) -> bool {
150 &self[..] == other
151 }
152}
153
154#[allow(clippy::derive_hash_xor_eq)]
155impl<T> hash::Hash for String<T>
156where
157 T: AsRef<[u8]>,
158{
159 fn hash<H: hash::Hasher>(&self, state: &mut H) {
160 ops::Deref::deref(self).hash(state);
161 }
162}
163
164impl<T> ops::Deref for String<T>
165where
166 T: AsRef<[u8]>,
167{
168 type Target = str;
169
170 #[inline]
171 fn deref(&self) -> &str {
172 let b = self.value.as_ref();
173 // SAFETY: The `StableAsRef` marker trait ensures that
174 // the impl of `AsRef<[u8]>` for `T` behaves sanely.
175 unsafe { str::from_utf8_unchecked(b) }
176 }
177}
178
179impl<T> ops::DerefMut for String<T>
180where
181 T: AsRef<[u8]> + AsMut<[u8]>,
182{
183 #[inline]
184 fn deref_mut(&mut self) -> &mut str {
185 let b = self.value.as_mut();
186 // SAFETY: The `StableAsRef` marker trait ensures that
187 // the impl of `AsMut<[u8]>` for `T` behaves sanely.
188 unsafe { str::from_utf8_unchecked_mut(b) }
189 }
190}
191
192impl<T> borrow::Borrow<str> for String<T>
193where
194 T: AsRef<[u8]>,
195{
196 fn borrow(&self) -> &str {
197 &*self
198 }
199}
200
201impl From<::std::string::String> for String<::std::string::String> {
202 fn from(value: ::std::string::String) -> Self {
203 String { value }
204 }
205}
206
207impl<T> Default for String<T>
208where
209 T: Default + StableAsRef,
210{
211 fn default() -> Self {
212 String {
213 value: T::default(),
214 }
215 }
216}
217
218impl<T> TryFrom<T> for String<T>
219where
220 T: AsRef<[u8]> + StableAsRef,
221{
222 type Error = str::Utf8Error;
223
224 fn try_from(value: T) -> Result<Self, Self::Error> {
225 let _ = str::from_utf8(value.as_ref())?;
226 Ok(String { value })
227 }
228}
229
230impl<T: AsRef<[u8]>> fmt::Debug for String<T> {
231 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
232 (**self).fmt(fmt)
233 }
234}
235
236impl<T: AsRef<[u8]>> fmt::Display for String<T> {
237 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
238 (**self).fmt(fmt)
239 }
240}
241
242#[cfg(feature = "valuable")]
243impl<T> inspect::Valuable for String<T>
244where
245 T: AsRef<[u8]>,
246{
247 fn as_value(&self) -> inspect::Value<'_> {
248 inspect::Value::String(&self[..])
249 }
250
251 fn visit(&self, visit: &mut dyn inspect::Visit) {
252 visit.visit_value(inspect::Value::String(self));
253 }
254}
255
256/// Attempt to construct `Self` via a conversion.
257///
258/// This trait will be deprecated in favor of [std::convert::TryFrom] once it
259/// reaches stable Rust.
260pub trait TryFrom<T>: Sized + sealed::Sealed {
261 /// The type returned in the event of a conversion error.
262 type Error;
263
264 /// Performs the conversion.
265 fn try_from(value: T) -> Result<Self, Self::Error>;
266}
267
268impl<T> sealed::Sealed for String<T> {}
269
270mod sealed {
271 /// Private trait to this crate to prevent traits from being implemented in
272 /// downstream crates.
273 pub trait Sealed {}
274}
275
276/// Marker trait that indicates that a type is guaranteed safe to use as backing storage
277/// for `String`.
278///
279/// In order to be safe, a storage type `T` needs to guarantee the following:
280///
281/// - If `T` implements `AsRef<[u8]>` and/or `AsMut<[u8]>`, the contents of `T` as visible
282/// the byte slice returned by `as_ref` and `as_mut` may only be mutated through mutable
283/// references or owned access. In other words, no use of interior mutability.
284///
285/// - If `T` implements `AsRef<[u8]>`, the `as_ref` method must always return the same
286/// slice of bytes (unless the storage is mutated).
287///
288/// - If `T` implements `AsRef<[u8]>` and `AsMut<[u8]>`, the `as_mut` method must return
289/// a mutable reference to the same slice of bytes as the `as_ref` method returns.
290///
291/// - If `T` implements `AsRef<[u8]>` and `Default`, the default value must represent the
292/// empty byte sequence. In other words, `T::default().as_ref().len() == 0`.
293///
294/// - If `T` implements `AsRef<[u8]>` and `From<&[u8]>`, it must do so in such a way that
295/// the byte slice returned by `as_ref` is equal to the byte slice provided to the `from`
296/// method.
297pub unsafe trait StableAsRef {}
298
299unsafe impl<'a, T> StableAsRef for &'a T where T: StableAsRef {}
300unsafe impl<'a, T> StableAsRef for &'a mut T where T: StableAsRef {}
301unsafe impl<T> StableAsRef for Box<T> where T: StableAsRef {}
302unsafe impl<T> StableAsRef for std::rc::Rc<T> where T: StableAsRef {}
303unsafe impl<T> StableAsRef for std::sync::Arc<T> where T: StableAsRef {}
304
305unsafe impl StableAsRef for std::string::String {}
306unsafe impl StableAsRef for str {}
307unsafe impl StableAsRef for Vec<u8> {}
308unsafe impl StableAsRef for [u8] {}
309
310#[cfg(feature = "bytes")]
311unsafe impl StableAsRef for bytes::Bytes {}
312
313#[cfg(feature = "bytes")]
314unsafe impl StableAsRef for bytes::BytesMut {}
315
316macro_rules! array_impls {
317 ($($len:expr)+) => {
318 $(
319 unsafe impl StableAsRef for [u8; $len] {}
320 )+
321 }
322}
323
324array_impls!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
325
326#[cfg(test)]
327mod test {
328 use super::*;
329
330 #[test]
331 fn test_from_std_string() {
332 let s: String<_> = "hello".to_string().into();
333 assert_eq!(&s, "hello");
334 }
335
336 #[test]
337 fn test_from_str() {
338 let _: String<Vec<u8>> = String::from_str("nice str");
339 }
340
341 #[test]
342 fn test_try_from_bytes() {
343 let _ = String::try_from(b"nice bytes").unwrap();
344 }
345}