A flexible, simple to use, immutable, clone-efficient String replacement for
Rust
Rust is great, but it's String type is not optimized for typical string
use cases, but as a mutable string buffer. Most string use cases don't
modify their string contents, often need to copy strings around as if
they were cheap like integers, typically concatenate instead of modify, and
often end up being cloned with identical contents. Additionally, String
isn't able to wrap a string literal without additional allocation and copying.
Rust needs a new string type to unify usage of both literals and
allocated strings in typical use cases. This crate creates a new string type
that is optimized for those use cases, while retaining the usage simplicity of
String.
This type is not inherently "better" than String, however, but different. It
is a higher level type, that can at times mean higher overhead. It really
depends on the use case.
String (64-bit: 24 bytes)std)FlexStr
&'static str), inlined strings
(InlineFlexStr), or an Rc wrapped str Send or Sync (due to usage of Rc)AFlexStr
FlexStr but uses Arc instead of Rc for the wrapped
strSend and SyncInlineFlexStr
FlexStr and AFlexStr - not typically
used directly```rust use flexstr::IntoFlexStr;
fn main() { // Literal - no copying or allocation let hello = "world!".into_flexstr();
println!("Hello {world}"); } ```
```rust use flexstr::{IntoAFlexStr, IntoFlexStr, ToFlexStr};
fn main() {
// From literal - no copying or allocation
// NOTE: to_flexstr will copy, so use into_flexstr for literals
let literal = "literal".into_flexstr();
// From borrowed string - Copied into inline string
let owned = "inlined".to_string();
let str_to_inlined = (&owned).to_flexstr();
// From borrowed String - copied into `str` wrapped in `Rc`
let owned = "A bit too long to be inlined!!!".to_string();
let str_to_wrapped = (&owned).to_flexstr();
// From String - copied into inline string (`String` storage released)
let inlined = "inlined".to_string().into_flexstr();
// From String - `str` wrapped in `Rc` (`String` storage released)
let counted = "A bit too long to be inlined!!!".to_string().into_flexstr();
// *** If you want a Send/Sync type you need `AFlexStr` instead ***
// From FlexStr wrapped literal - no copying or allocation
let literal = literal.into_a_flexstr();
// From FlexStr inlined string - no allocation
let inlined = inlined.into_a_flexstr();
// From FlexStr `Rc` wrapped `str` - copies into `str` wrapped in `Arc`
let counted = counted.into_a_flexstr();
} ```
Works just like String
NOTE: The only benefit to passing as a &str is more compatibility with
existing code. By passing as a &FlexStr instead, we retain the possibility
of cheap multi ownership (see below).
```rust use flexstr::FlexStr;
fn my_func(str: &FlexStr) { println!("Borrowed string: {str}"); }
fn main() { // Literal - no copy or allocation let str: FlexStr = "my string".into(); my_func(&str); } ```
This has always been a confusing situation in Rust, but it is easy with
FlexStr since multi ownership is cheap.
```rust use flexstr::{IntoFlexStr, FlexStr};
struct MyStruct { s: FlexStr }
impl MyStruct { fn toownornottoown(s: &FlexStr) -> Self { let s = if s == "ownme" { // Since a wrapped literal, no copy or allocation s.clone() } else { // Wrapped literal - no copy or allocation "own_me".into() };
Self { s }
}
}
fn main() { // Wrapped literals - no copy or allocation let s = "borrow me".intoflexstr(); let s2 = "own me".intoflexstr();
let struct1 = MyStruct::to_own_or_not_to_own(&s);
let struct2 = MyStruct::to_own_or_not_to_own(&s2);
assert_eq!(s2, struct1.s);
assert_eq!(s2, struct2.s);
} ```
NOTE: No benchmarking has yet been done
into() and never copiedinto() on a String will result in an inline string (if
short) otherwise copied into a str wrapped in Rc/Arc
(which will allocate, copy, and then release original String storage)into_string() and into_a_string() are equivalent to calling into()
on both literals and String (they are present primarily for let
bindings without needing to declare type)to_flexstr() and to_a_flexstr() are meant for the on-boarding of borrowed
strings and always copy into either an inline string (for short strings) or
an Rc/Arc wrapped str (which will allocate)to_string always copies into a new StringAFlexStr and FlexStr using into()
are cheap when using wrapped literals or inlined strings
Rc or ArcThere is no free lunch:
Rc (or Arc), when on-boarding String it will need to
reallocate and copyFlexStr is not Send or Sync, there is a need to consider
single-threaded (FlexStr) and multi-threaded (AFlexStr) use cases and
convert accordinglyThis is currently Alpha quality and in heavy development. There is much testing and design work still needed. The API may break at any time.
This project is licensed optionally under either: