smartstring/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! # Smart String
6//!
7//! [`SmartString`] is a wrapper around [`String`] which offers
8//! automatic inlining of small strings. It comes in two flavours:
9//! [`LazyCompact`], which takes up exactly as much space as a [`String`]
10//! and is generally a little faster, and [`Compact`], which is the same as
11//! [`LazyCompact`] except it will aggressively re-inline any expanded
12//! [`String`]s which become short enough to do so.
13//! [`LazyCompact`] is the default, and what you should be using unless
14//! you care considerably more about heap memory usage than performance.
15//!
16//! ## What Is It For?
17//!
18//! The intended use for [`SmartString`] is as a key type for a
19//! B-tree (such as [`std::collections::BTreeMap`]) or any kind of
20//! array operation where cache locality is critical.
21//!
22//! In general, it's a nice data type for reducing your heap allocations and
23//! increasing the locality of string data. If you use [`SmartString`]
24//! as a drop-in replacement for [`String`], you're almost certain to see
25//! a slight performance boost, as well as slightly reduced memory usage.
26//!
27//! ## How To Use It?
28//!
29//! [`SmartString`] has the exact same API as [`String`],
30//! all the clever bits happen automatically behind the scenes, so you could just:
31//!
32//! ```rust
33//! use smartstring::alias::String;
34//! use std::fmt::Write;
35//!
36//! let mut string = String::new();
37//! string.push_str("This is just a string!");
38//! string.clear();
39//! write!(string, "Hello Joe!");
40//! assert_eq!("Hello Joe!", string);
41//! ```
42//!
43//! ## Give Me The Details
44//!
45//! [`SmartString`] is the same size as [`String`] and
46//! relies on pointer alignment to be able to store a discriminant bit in its
47//! inline form that will never be present in its [`String`] form, thus
48//! giving us 24 bytes (on 64-bit architectures) minus one bit to encode our
49//! inline string. It uses 23 bytes to store the string data and the remaining
50//! 7 bits to encode the string's length. When the available space is exceeded,
51//! it swaps itself out with a boxed string type containing its previous
52//! contents. Likewise, if the string's length should drop below its inline
53//! capacity again, it deallocates the string and moves its contents inline.
54//!
55//! In [`Compact`] mode, it is aggressive about inlining strings, meaning that if you modify a heap allocated
56//! string such that it becomes short enough for inlining, it will be inlined immediately
57//! and the allocated [`String`] will be dropped. This may cause multiple
58//! unintended allocations if you repeatedly adjust your string's length across the
59//! inline capacity threshold, so if your string's construction can get
60//! complicated and you're relying on performance during construction, it might be better
61//! to construct it as a [`String`] and convert it once construction is done.
62//!
63//! [`LazyCompact`] looks the same as [`Compact`], except
64//! it never re-inlines a string that's already been heap allocated, instead
65//! keeping the allocation around in case it needs it. This makes for less
66//! cache local strings, but is the best choice if you're more worried about
67//! time spent on unnecessary allocations than cache locality.
68//!
69//! ## Performance
70//!
71//! It doesn't aim to be more performant than [`String`] in the general case,
72//! except that it doesn't trigger heap allocations for anything shorter than
73//! its inline capacity and so can be reasonably expected to exceed
74//! [`String`]'s performance perceptibly on shorter strings, as well as being more
75//! memory efficient in these cases. There will always be a slight overhead on all
76//! operations on boxed strings, compared to [`String`].
77//!
78//! ## Feature Flags
79//!
80//! `smartstring` comes with optional support for the following crates through Cargo
81//! feature flags. You can enable them in your `Cargo.toml` file like this:
82//!
83//! ```no_compile
84//! [dependencies]
85//! smartstring = { version = "*", features = ["proptest", "serde"] }
86//! ```
87//!
88//! | Feature | Description |
89//! | ------- | ----------- |
90//! | [`arbitrary`](https://crates.io/crates/arbitrary) | [`Arbitrary`][Arbitrary] implementation for [`SmartString`]. |
91//! | [`proptest`](https://crates.io/crates/proptest) | A strategy for generating [`SmartString`]s from a regular expression. |
92//! | [`serde`](https://crates.io/crates/serde) | [`Serialize`][Serialize] and [`Deserialize`][Deserialize] implementations for [`SmartString`]. |
93//!
94//! [Serialize]: https://docs.rs/serde/latest/serde/trait.Serialize.html
95//! [Deserialize]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
96//! [Arbitrary]: https://docs.rs/arbitrary/latest/arbitrary/trait.Arbitrary.html
97
98// Ensure all unsafe blocks get flagged for manual validation.
99#![deny(unsafe_code)]
100#![forbid(rust_2018_idioms)]
101#![deny(nonstandard_style)]
102#![warn(unreachable_pub, missing_debug_implementations, missing_docs)]
103#![cfg_attr(not(feature = "std"), no_std)]
104#![cfg_attr(needs_allocator_feature, feature(allocator_api))]
105
106extern crate alloc;
107
108use alloc::{
109    boxed::Box,
110    string::{String, ToString},
111};
112use core::{
113    borrow::{Borrow, BorrowMut},
114    cmp::Ordering,
115    convert::Infallible,
116    fmt::{Debug, Display, Error, Formatter, Write},
117    hash::{Hash, Hasher},
118    iter::FromIterator,
119    marker::PhantomData,
120    mem::{forget, MaybeUninit},
121    ops::{
122        Add, Deref, DerefMut, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull,
123        RangeInclusive, RangeTo, RangeToInclusive,
124    },
125    ptr::drop_in_place,
126    str::FromStr,
127};
128
129#[cfg(feature = "std")]
130use std::borrow::Cow;
131
132mod config;
133pub use config::{Compact, LazyCompact, SmartStringMode, MAX_INLINE};
134
135mod marker_byte;
136use marker_byte::Discriminant;
137
138mod inline;
139use inline::InlineString;
140
141mod boxed;
142use boxed::BoxedString;
143
144mod casts;
145use casts::{StringCast, StringCastInto, StringCastMut};
146
147mod iter;
148pub use iter::Drain;
149
150mod ops;
151use ops::{string_op_grow, string_op_shrink};
152
153#[cfg(feature = "serde")]
154mod serde;
155
156#[cfg(feature = "arbitrary")]
157mod arbitrary;
158
159#[cfg(feature = "proptest")]
160pub mod proptest;
161
162/// Convenient type aliases.
163pub mod alias {
164    use super::*;
165
166    /// A convenience alias for a [`LazyCompact`] layout [`SmartString`].
167    ///
168    /// Just pretend it's a [`String`][String]!
169    pub type String = SmartString<LazyCompact>;
170
171    /// A convenience alias for a [`Compact`] layout [`SmartString`].
172    pub type CompactString = SmartString<Compact>;
173}
174
175/// A smart string.
176///
177/// This wraps one of two string types: an inline string or a boxed string.
178/// Conversion between the two happens opportunistically and transparently.
179///
180/// It takes a layout as its type argument: one of [`Compact`] or [`LazyCompact`].
181///
182/// It mimics the interface of [`String`] except where behaviour cannot
183/// be guaranteed to stay consistent between its boxed and inline states. This means
184/// you still have `capacity()` and `shrink_to_fit()`, relating to state that only
185/// really exists in the boxed variant, because the inline variant can still give
186/// sensible behaviour for these operations, but `with_capacity()`, `reserve()` etc are
187/// absent, because they would have no effect on inline strings and the requested
188/// state changes wouldn't carry over if the inline string is promoted to a boxed
189/// one - not without also storing that state in the inline representation, which
190/// would waste precious bytes for inline string data.
191pub struct SmartString<Mode: SmartStringMode> {
192    data: MaybeUninit<InlineString>,
193    mode: PhantomData<Mode>,
194}
195
196impl<Mode: SmartStringMode> Drop for SmartString<Mode> {
197    fn drop(&mut self) {
198        if let StringCastMut::Boxed(string) = self.cast_mut() {
199            #[allow(unsafe_code)]
200            unsafe {
201                drop_in_place(string)
202            };
203        }
204    }
205}
206
207impl<Mode: SmartStringMode> Clone for SmartString<Mode> {
208    /// Clone a [`SmartString`].
209    ///
210    /// If the string is inlined, this is a [`Copy`] operation. Otherwise,
211    /// a string with the same capacity as the source is allocated.
212    fn clone(&self) -> Self {
213        match self.cast() {
214            StringCast::Boxed(string) => Self::from_boxed(string.clone()),
215            StringCast::Inline(string) => Self::from_inline(*string),
216        }
217    }
218}
219
220impl<Mode: SmartStringMode> Deref for SmartString<Mode> {
221    type Target = str;
222
223    #[inline(always)]
224    fn deref(&self) -> &Self::Target {
225        match self.cast() {
226            StringCast::Boxed(string) => string.deref(),
227            StringCast::Inline(string) => string.deref(),
228        }
229    }
230}
231
232impl<Mode: SmartStringMode> DerefMut for SmartString<Mode> {
233    #[inline(always)]
234    fn deref_mut(&mut self) -> &mut Self::Target {
235        match self.cast_mut() {
236            StringCastMut::Boxed(string) => string.deref_mut(),
237            StringCastMut::Inline(string) => string.deref_mut(),
238        }
239    }
240}
241
242impl SmartString<LazyCompact> {
243    /// Construct an empty string.
244    ///
245    /// This is a `const fn` version of [`SmartString::new`].
246    /// It's a temporary measure while we wait for trait bounds on
247    /// type arguments to `const fn`s to stabilise, and will be deprecated
248    /// once this happens.
249    pub const fn new_const() -> Self {
250        Self {
251            data: MaybeUninit::new(InlineString::new()),
252            mode: PhantomData,
253        }
254    }
255}
256
257impl SmartString<Compact> {
258    /// Construct an empty string.
259    ///
260    /// This is a `const fn` version of [`SmartString::new`].
261    /// It's a temporary measure while we wait for trait bounds on
262    /// type arguments to `const fn`s to stabilise, and will be deprecated
263    /// once this happens.
264    pub const fn new_const() -> Self {
265        Self {
266            data: MaybeUninit::new(InlineString::new()),
267            mode: PhantomData,
268        }
269    }
270}
271
272impl<Mode: SmartStringMode> SmartString<Mode> {
273    /// Construct an empty string.
274    #[inline(always)]
275    pub fn new() -> Self {
276        Self::from_inline(InlineString::new())
277    }
278
279    fn from_boxed(boxed: BoxedString) -> Self {
280        let mut out = Self {
281            data: MaybeUninit::uninit(),
282            mode: PhantomData,
283        };
284        let data_ptr: *mut BoxedString = out.data.as_mut_ptr().cast();
285        #[allow(unsafe_code)]
286        unsafe {
287            data_ptr.write(boxed)
288        };
289        out
290    }
291
292    fn from_inline(inline: InlineString) -> Self {
293        Self {
294            data: MaybeUninit::new(inline),
295            mode: PhantomData,
296        }
297    }
298
299    fn discriminant(&self) -> Discriminant {
300        // unsafe { self.data.assume_init() }.marker.discriminant()
301        let str_ptr: *const BoxedString =
302            self.data.as_ptr().cast() as *const _ as *const BoxedString;
303        #[allow(unsafe_code)]
304        Discriminant::from_bit(BoxedString::check_alignment(unsafe { &*str_ptr }))
305    }
306
307    fn cast(&self) -> StringCast<'_> {
308        #[allow(unsafe_code)]
309        match self.discriminant() {
310            Discriminant::Inline => StringCast::Inline(unsafe { &*self.data.as_ptr() }),
311            Discriminant::Boxed => StringCast::Boxed(unsafe { &*self.data.as_ptr().cast() }),
312        }
313    }
314
315    fn cast_mut(&mut self) -> StringCastMut<'_> {
316        #[allow(unsafe_code)]
317        match self.discriminant() {
318            Discriminant::Inline => StringCastMut::Inline(unsafe { &mut *self.data.as_mut_ptr() }),
319            Discriminant::Boxed => {
320                StringCastMut::Boxed(unsafe { &mut *self.data.as_mut_ptr().cast() })
321            }
322        }
323    }
324
325    fn cast_into(mut self) -> StringCastInto {
326        #[allow(unsafe_code)]
327        match self.discriminant() {
328            Discriminant::Inline => StringCastInto::Inline(unsafe { self.data.assume_init() }),
329            Discriminant::Boxed => StringCastInto::Boxed(unsafe {
330                let boxed_ptr: *mut BoxedString = self.data.as_mut_ptr().cast();
331                let string = boxed_ptr.read();
332                forget(self);
333                string
334            }),
335        }
336    }
337
338    fn promote_from(&mut self, string: BoxedString) {
339        debug_assert!(self.discriminant() == Discriminant::Inline);
340        let data: *mut BoxedString = self.data.as_mut_ptr().cast();
341        #[allow(unsafe_code)]
342        unsafe {
343            data.write(string)
344        };
345    }
346
347    /// Attempt to inline the string if it's currently heap allocated.
348    ///
349    /// Returns the resulting state: `true` if it's inlined, `false` if it's not.
350    fn try_demote(&mut self) -> bool {
351        if Mode::DEALLOC {
352            self.really_try_demote()
353        } else {
354            false
355        }
356    }
357
358    /// Attempt to inline the string regardless of whether `Mode::DEALLOC` is set.
359    fn really_try_demote(&mut self) -> bool {
360        if let StringCastMut::Boxed(string) = self.cast_mut() {
361            if string.len() > MAX_INLINE {
362                false
363            } else {
364                let s: &str = string.deref();
365                let inlined = s.into();
366                #[allow(unsafe_code)]
367                unsafe {
368                    drop_in_place(string);
369                    self.data.as_mut_ptr().write(inlined);
370                }
371                true
372            }
373        } else {
374            true
375        }
376    }
377
378    /// Return the length in bytes of the string.
379    ///
380    /// Note that this may differ from the length in `char`s.
381    pub fn len(&self) -> usize {
382        match self.cast() {
383            StringCast::Boxed(string) => string.len(),
384            StringCast::Inline(string) => string.len(),
385        }
386    }
387
388    /// Test whether the string is empty.
389    pub fn is_empty(&self) -> bool {
390        self.len() == 0
391    }
392
393    /// Test whether the string is currently inlined.
394    pub fn is_inline(&self) -> bool {
395        self.discriminant() == Discriminant::Inline
396    }
397
398    /// Get a reference to the string as a string slice.
399    pub fn as_str(&self) -> &str {
400        self.deref()
401    }
402
403    /// Get a reference to the string as a mutable string slice.
404    pub fn as_mut_str(&mut self) -> &mut str {
405        self.deref_mut()
406    }
407
408    /// Return the currently allocated capacity of the string.
409    ///
410    /// Note that if this is a boxed string, it returns [`String::capacity()`][String::capacity],
411    /// but an inline string always returns [`MAX_INLINE`].
412    ///
413    /// Note also that if a boxed string is converted into an inline string, its capacity is
414    /// deallocated, and if the inline string is promoted to a boxed string in the future,
415    /// it will be reallocated with a default capacity.
416    pub fn capacity(&self) -> usize {
417        if let StringCast::Boxed(string) = self.cast() {
418            string.capacity()
419        } else {
420            MAX_INLINE
421        }
422    }
423
424    /// Push a character to the end of the string.
425    pub fn push(&mut self, ch: char) {
426        string_op_grow!(ops::Push, self, ch)
427    }
428
429    /// Copy a string slice onto the end of the string.
430    pub fn push_str(&mut self, string: &str) {
431        string_op_grow!(ops::PushStr, self, string)
432    }
433
434    /// Shrink the capacity of the string to fit its contents exactly.
435    ///
436    /// This has no effect on inline strings, which always have a fixed capacity.
437    /// Thus, it's not safe to assume that [`capacity()`][SmartString::capacity] will
438    /// equal [`len()`][SmartString::len] after calling this.
439    ///
440    /// Calling this on a [`LazyCompact`] string that is currently
441    /// heap allocated but is short enough to be inlined will deallocate the
442    /// heap allocation and convert it to an inline string.
443    pub fn shrink_to_fit(&mut self) {
444        if let StringCastMut::Boxed(string) = self.cast_mut() {
445            if string.len() > MAX_INLINE {
446                string.shrink_to_fit();
447            }
448        }
449        self.really_try_demote();
450    }
451
452    /// Truncate the string to `new_len` bytes.
453    ///
454    /// If `new_len` is larger than the string's current length, this does nothing.
455    /// If `new_len` isn't on a UTF-8 character boundary, this method panics.
456    pub fn truncate(&mut self, new_len: usize) {
457        string_op_shrink!(ops::Truncate, self, new_len)
458    }
459
460    /// Pop a `char` off the end of the string.
461    pub fn pop(&mut self) -> Option<char> {
462        string_op_shrink!(ops::Pop, self)
463    }
464
465    /// Remove a `char` from the string at the given index.
466    ///
467    /// If the index doesn't fall on a UTF-8 character boundary, this method panics.
468    pub fn remove(&mut self, index: usize) -> char {
469        string_op_shrink!(ops::Remove, self, index)
470    }
471
472    /// Insert a `char` into the string at the given index.
473    ///
474    /// If the index doesn't fall on a UTF-8 character boundary, this method panics.
475    pub fn insert(&mut self, index: usize, ch: char) {
476        string_op_grow!(ops::Insert, self, index, ch)
477    }
478
479    /// Insert a string slice into the string at the given index.
480    ///
481    /// If the index doesn't fall on a UTF-8 character boundary, this method panics.
482    pub fn insert_str(&mut self, index: usize, string: &str) {
483        string_op_grow!(ops::InsertStr, self, index, string)
484    }
485
486    /// Split the string into two at the given index.
487    ///
488    /// Returns the content to the right of the index as a new string, and removes
489    /// it from the original.
490    ///
491    /// If the index doesn't fall on a UTF-8 character boundary, this method panics.
492    pub fn split_off(&mut self, index: usize) -> Self {
493        string_op_shrink!(ops::SplitOff<Mode>, self, index)
494    }
495
496    /// Clear the string.
497    ///
498    /// This causes any memory reserved by the string to be immediately deallocated.
499    pub fn clear(&mut self) {
500        *self = Self::new();
501    }
502
503    /// Filter out `char`s not matching a predicate.
504    pub fn retain<F>(&mut self, f: F)
505    where
506        F: FnMut(char) -> bool,
507    {
508        string_op_shrink!(ops::Retain, self, f)
509    }
510
511    /// Construct a draining iterator over a given range.
512    ///
513    /// This removes the given range from the string, and returns an iterator over the
514    /// removed `char`s.
515    pub fn drain<R>(&mut self, range: R) -> Drain<'_, Mode>
516    where
517        R: RangeBounds<usize>,
518    {
519        Drain::new(self, range)
520    }
521
522    /// Replaces a range with the contents of a string slice.
523    pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
524    where
525        R: RangeBounds<usize>,
526    {
527        string_op_grow!(ops::ReplaceRange, self, &range, replace_with);
528        self.try_demote();
529    }
530}
531
532impl<Mode: SmartStringMode> Default for SmartString<Mode> {
533    fn default() -> Self {
534        Self::new()
535    }
536}
537
538impl<Mode: SmartStringMode> AsRef<str> for SmartString<Mode> {
539    fn as_ref(&self) -> &str {
540        self.deref()
541    }
542}
543
544impl<Mode: SmartStringMode> AsMut<str> for SmartString<Mode> {
545    fn as_mut(&mut self) -> &mut str {
546        self.deref_mut()
547    }
548}
549
550impl<Mode: SmartStringMode> AsRef<[u8]> for SmartString<Mode> {
551    fn as_ref(&self) -> &[u8] {
552        self.deref().as_bytes()
553    }
554}
555
556impl<Mode: SmartStringMode> Borrow<str> for SmartString<Mode> {
557    fn borrow(&self) -> &str {
558        self.deref()
559    }
560}
561
562impl<Mode: SmartStringMode> BorrowMut<str> for SmartString<Mode> {
563    fn borrow_mut(&mut self) -> &mut str {
564        self.deref_mut()
565    }
566}
567
568impl<Mode: SmartStringMode> Index<Range<usize>> for SmartString<Mode> {
569    type Output = str;
570    fn index(&self, index: Range<usize>) -> &Self::Output {
571        &self.deref()[index]
572    }
573}
574
575impl<Mode: SmartStringMode> Index<RangeTo<usize>> for SmartString<Mode> {
576    type Output = str;
577    fn index(&self, index: RangeTo<usize>) -> &Self::Output {
578        &self.deref()[index]
579    }
580}
581
582impl<Mode: SmartStringMode> Index<RangeFrom<usize>> for SmartString<Mode> {
583    type Output = str;
584    fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
585        &self.deref()[index]
586    }
587}
588
589impl<Mode: SmartStringMode> Index<RangeFull> for SmartString<Mode> {
590    type Output = str;
591    fn index(&self, _index: RangeFull) -> &Self::Output {
592        self.deref()
593    }
594}
595
596impl<Mode: SmartStringMode> Index<RangeInclusive<usize>> for SmartString<Mode> {
597    type Output = str;
598    fn index(&self, index: RangeInclusive<usize>) -> &Self::Output {
599        &self.deref()[index]
600    }
601}
602
603impl<Mode: SmartStringMode> Index<RangeToInclusive<usize>> for SmartString<Mode> {
604    type Output = str;
605    fn index(&self, index: RangeToInclusive<usize>) -> &Self::Output {
606        &self.deref()[index]
607    }
608}
609
610impl<Mode: SmartStringMode> IndexMut<Range<usize>> for SmartString<Mode> {
611    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
612        &mut self.deref_mut()[index]
613    }
614}
615
616impl<Mode: SmartStringMode> IndexMut<RangeTo<usize>> for SmartString<Mode> {
617    fn index_mut(&mut self, index: RangeTo<usize>) -> &mut Self::Output {
618        &mut self.deref_mut()[index]
619    }
620}
621
622impl<Mode: SmartStringMode> IndexMut<RangeFrom<usize>> for SmartString<Mode> {
623    fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut Self::Output {
624        &mut self.deref_mut()[index]
625    }
626}
627
628impl<Mode: SmartStringMode> IndexMut<RangeFull> for SmartString<Mode> {
629    fn index_mut(&mut self, _index: RangeFull) -> &mut Self::Output {
630        self.deref_mut()
631    }
632}
633
634impl<Mode: SmartStringMode> IndexMut<RangeInclusive<usize>> for SmartString<Mode> {
635    fn index_mut(&mut self, index: RangeInclusive<usize>) -> &mut Self::Output {
636        &mut self.deref_mut()[index]
637    }
638}
639
640impl<Mode: SmartStringMode> IndexMut<RangeToInclusive<usize>> for SmartString<Mode> {
641    fn index_mut(&mut self, index: RangeToInclusive<usize>) -> &mut Self::Output {
642        &mut self.deref_mut()[index]
643    }
644}
645
646impl<Mode: SmartStringMode> From<&'_ str> for SmartString<Mode> {
647    fn from(string: &'_ str) -> Self {
648        if string.len() > MAX_INLINE {
649            Self::from_boxed(string.to_string().into())
650        } else {
651            Self::from_inline(string.into())
652        }
653    }
654}
655
656impl<Mode: SmartStringMode> From<&'_ mut str> for SmartString<Mode> {
657    fn from(string: &'_ mut str) -> Self {
658        if string.len() > MAX_INLINE {
659            Self::from_boxed(string.to_string().into())
660        } else {
661            Self::from_inline(string.deref().into())
662        }
663    }
664}
665
666impl<Mode: SmartStringMode> From<&'_ String> for SmartString<Mode> {
667    fn from(string: &'_ String) -> Self {
668        if string.len() > MAX_INLINE {
669            Self::from_boxed(string.clone().into())
670        } else {
671            Self::from_inline(string.deref().into())
672        }
673    }
674}
675
676impl<Mode: SmartStringMode> From<String> for SmartString<Mode> {
677    fn from(string: String) -> Self {
678        if string.len() > MAX_INLINE {
679            Self::from_boxed(string.into())
680        } else {
681            Self::from_inline(string.deref().into())
682        }
683    }
684}
685
686impl<Mode: SmartStringMode> From<Box<str>> for SmartString<Mode> {
687    fn from(string: Box<str>) -> Self {
688        if string.len() > MAX_INLINE {
689            String::from(string).into()
690        } else {
691            Self::from(&*string)
692        }
693    }
694}
695
696#[cfg(feature = "std")]
697impl<Mode: SmartStringMode> From<Cow<'_, str>> for SmartString<Mode> {
698    fn from(string: Cow<'_, str>) -> Self {
699        if string.len() > MAX_INLINE {
700            String::from(string).into()
701        } else {
702            Self::from(&*string)
703        }
704    }
705}
706
707impl<'a, Mode: SmartStringMode> Extend<&'a str> for SmartString<Mode> {
708    fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
709        for item in iter {
710            self.push_str(item);
711        }
712    }
713}
714
715impl<'a, Mode: SmartStringMode> Extend<&'a char> for SmartString<Mode> {
716    fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
717        for item in iter {
718            self.push(*item);
719        }
720    }
721}
722
723impl<Mode: SmartStringMode> Extend<char> for SmartString<Mode> {
724    fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
725        for item in iter {
726            self.push(item);
727        }
728    }
729}
730
731impl<Mode: SmartStringMode> Extend<SmartString<Mode>> for SmartString<Mode> {
732    fn extend<I: IntoIterator<Item = SmartString<Mode>>>(&mut self, iter: I) {
733        for item in iter {
734            self.push_str(&item);
735        }
736    }
737}
738
739impl<Mode: SmartStringMode> Extend<String> for SmartString<Mode> {
740    fn extend<I: IntoIterator<Item = String>>(&mut self, iter: I) {
741        for item in iter {
742            self.push_str(&item);
743        }
744    }
745}
746
747impl<'a, Mode: SmartStringMode + 'a> Extend<&'a SmartString<Mode>> for SmartString<Mode> {
748    fn extend<I: IntoIterator<Item = &'a SmartString<Mode>>>(&mut self, iter: I) {
749        for item in iter {
750            self.push_str(item);
751        }
752    }
753}
754
755impl<'a, Mode: SmartStringMode> Extend<&'a String> for SmartString<Mode> {
756    fn extend<I: IntoIterator<Item = &'a String>>(&mut self, iter: I) {
757        for item in iter {
758            self.push_str(item);
759        }
760    }
761}
762
763impl<Mode: SmartStringMode> Add<Self> for SmartString<Mode> {
764    type Output = Self;
765    fn add(mut self, rhs: Self) -> Self::Output {
766        self.push_str(&rhs);
767        self
768    }
769}
770
771impl<Mode: SmartStringMode> Add<&'_ Self> for SmartString<Mode> {
772    type Output = Self;
773    fn add(mut self, rhs: &'_ Self) -> Self::Output {
774        self.push_str(rhs);
775        self
776    }
777}
778
779impl<Mode: SmartStringMode> Add<&'_ str> for SmartString<Mode> {
780    type Output = Self;
781    fn add(mut self, rhs: &'_ str) -> Self::Output {
782        self.push_str(rhs);
783        self
784    }
785}
786
787impl<Mode: SmartStringMode> Add<&'_ String> for SmartString<Mode> {
788    type Output = Self;
789    fn add(mut self, rhs: &'_ String) -> Self::Output {
790        self.push_str(rhs);
791        self
792    }
793}
794
795impl<Mode: SmartStringMode> Add<String> for SmartString<Mode> {
796    type Output = Self;
797    fn add(mut self, rhs: String) -> Self::Output {
798        self.push_str(&rhs);
799        self
800    }
801}
802
803impl<Mode: SmartStringMode> Add<SmartString<Mode>> for String {
804    type Output = Self;
805    fn add(mut self, rhs: SmartString<Mode>) -> Self::Output {
806        self.push_str(&rhs);
807        self
808    }
809}
810
811impl<Mode: SmartStringMode> FromIterator<Self> for SmartString<Mode> {
812    fn from_iter<I: IntoIterator<Item = Self>>(iter: I) -> Self {
813        let mut out = Self::new();
814        out.extend(iter.into_iter());
815        out
816    }
817}
818
819impl<Mode: SmartStringMode> FromIterator<String> for SmartString<Mode> {
820    fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
821        let mut out = Self::new();
822        out.extend(iter.into_iter());
823        out
824    }
825}
826
827impl<'a, Mode: SmartStringMode + 'a> FromIterator<&'a Self> for SmartString<Mode> {
828    fn from_iter<I: IntoIterator<Item = &'a Self>>(iter: I) -> Self {
829        let mut out = Self::new();
830        out.extend(iter.into_iter());
831        out
832    }
833}
834
835impl<'a, Mode: SmartStringMode> FromIterator<&'a str> for SmartString<Mode> {
836    fn from_iter<I: IntoIterator<Item = &'a str>>(iter: I) -> Self {
837        let mut out = Self::new();
838        out.extend(iter.into_iter());
839        out
840    }
841}
842
843impl<'a, Mode: SmartStringMode> FromIterator<&'a String> for SmartString<Mode> {
844    fn from_iter<I: IntoIterator<Item = &'a String>>(iter: I) -> Self {
845        let mut out = Self::new();
846        out.extend(iter.into_iter());
847        out
848    }
849}
850
851impl<Mode: SmartStringMode> FromIterator<char> for SmartString<Mode> {
852    fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
853        let mut out = Self::new();
854        for ch in iter {
855            out.push(ch);
856        }
857        out
858    }
859}
860
861impl<Mode: SmartStringMode> FromStr for SmartString<Mode> {
862    type Err = Infallible;
863    fn from_str(s: &str) -> Result<Self, Self::Err> {
864        Ok(Self::from(s))
865    }
866}
867
868impl<Mode: SmartStringMode> From<SmartString<Mode>> for String {
869    /// Unwrap a boxed [`String`][String], or copy an inline string into a new [`String`][String].
870    ///
871    /// [String]: https://doc.rust-lang.org/std/string/struct.String.html
872    fn from(s: SmartString<Mode>) -> Self {
873        match s.cast_into() {
874            StringCastInto::Boxed(string) => string.into(),
875            StringCastInto::Inline(string) => string.to_string(),
876        }
877    }
878}
879
880impl<Mode: SmartStringMode> PartialEq<str> for SmartString<Mode> {
881    fn eq(&self, other: &str) -> bool {
882        self.as_str() == other
883    }
884}
885
886impl<Mode: SmartStringMode> PartialEq<&'_ str> for SmartString<Mode> {
887    fn eq(&self, other: &&str) -> bool {
888        self.as_str() == *other
889    }
890}
891
892impl<Mode: SmartStringMode> PartialEq<SmartString<Mode>> for &'_ str {
893    fn eq(&self, other: &SmartString<Mode>) -> bool {
894        other.eq(*self)
895    }
896}
897
898impl<Mode: SmartStringMode> PartialEq<SmartString<Mode>> for str {
899    fn eq(&self, other: &SmartString<Mode>) -> bool {
900        other.eq(self)
901    }
902}
903
904impl<Mode: SmartStringMode> PartialEq<String> for SmartString<Mode> {
905    fn eq(&self, other: &String) -> bool {
906        self.eq(other.as_str())
907    }
908}
909
910impl<Mode: SmartStringMode> PartialEq<SmartString<Mode>> for String {
911    fn eq(&self, other: &SmartString<Mode>) -> bool {
912        other.eq(self.as_str())
913    }
914}
915
916impl<Mode: SmartStringMode> PartialEq for SmartString<Mode> {
917    fn eq(&self, other: &Self) -> bool {
918        self.as_str() == other.as_str()
919    }
920}
921
922impl<Mode: SmartStringMode> Eq for SmartString<Mode> {}
923
924impl<Mode: SmartStringMode> PartialOrd<str> for SmartString<Mode> {
925    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
926        self.as_str().partial_cmp(other)
927    }
928}
929
930impl<Mode: SmartStringMode> PartialOrd for SmartString<Mode> {
931    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
932        self.partial_cmp(other.as_str())
933    }
934}
935
936impl<Mode: SmartStringMode> Ord for SmartString<Mode> {
937    fn cmp(&self, other: &Self) -> Ordering {
938        self.as_str().cmp(other.as_str())
939    }
940}
941
942impl<Mode: SmartStringMode> Hash for SmartString<Mode> {
943    fn hash<H: Hasher>(&self, state: &mut H) {
944        self.as_str().hash(state)
945    }
946}
947
948impl<Mode: SmartStringMode> Debug for SmartString<Mode> {
949    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
950        Debug::fmt(self.as_str(), f)
951    }
952}
953
954impl<Mode: SmartStringMode> Display for SmartString<Mode> {
955    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
956        Display::fmt(self.as_str(), f)
957    }
958}
959
960impl<Mode: SmartStringMode> Write for SmartString<Mode> {
961    fn write_str(&mut self, string: &str) -> Result<(), Error> {
962        self.push_str(string);
963        Ok(())
964    }
965}
966
967#[cfg(any(test, feature = "test"))]
968#[allow(missing_docs)]
969pub mod test;