{"id":13632517,"url":"https://github.com/nu11ptr/flexstr","last_synced_at":"2025-12-12T16:26:47.182Z","repository":{"id":39672606,"uuid":"459801758","full_name":"nu11ptr/flexstr","owner":"nu11ptr","description":"A flexible, simple to use, immutable, clone-efficient String replacement for Rust","archived":false,"fork":false,"pushed_at":"2024-01-29T11:44:17.000Z","size":525,"stargazers_count":146,"open_issues_count":6,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-26T10:46:07.818Z","etag":null,"topics":["inline","refcount","reference-counting","rust","string"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nu11ptr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-16T00:50:42.000Z","updated_at":"2024-06-11T16:38:54.131Z","dependencies_parsed_at":"2024-06-11T16:38:48.533Z","dependency_job_id":"ef648a9c-9b71-489d-9b2d-13fa100b5b60","html_url":"https://github.com/nu11ptr/flexstr","commit_stats":{"total_commits":104,"total_committers":1,"mean_commits":104.0,"dds":0.0,"last_synced_commit":"c1969dff2c359dec373b8026e530801647fd4742"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nu11ptr%2Fflexstr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nu11ptr%2Fflexstr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nu11ptr%2Fflexstr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nu11ptr%2Fflexstr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nu11ptr","download_url":"https://codeload.github.com/nu11ptr/flexstr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249414253,"owners_count":21267724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["inline","refcount","reference-counting","rust","string"],"created_at":"2024-08-01T22:03:05.557Z","updated_at":"2025-12-12T16:26:47.144Z","avatar_url":"https://github.com/nu11ptr.png","language":"Rust","readme":"# flexstr\n\n[![Crate](https://img.shields.io/crates/v/flexstr)](https://crates.io/crates/flexstr)\n[![Docs](https://docs.rs/flexstr/badge.svg)](https://docs.rs/flexstr)\n[![Build](https://github.com/nu11ptr/flexstr/workflows/CI/badge.svg)](https://github.com/nu11ptr/flexstr/actions)\n[![codecov](https://codecov.io/gh/nu11ptr/flexstr/branch/master/graph/badge.svg?token=yUZ8v2tKPd)](https://codecov.io/gh/nu11ptr/flexstr)\n[![MSRV](https://img.shields.io/badge/msrv-1.59-blue.svg)](https://crates.io/crates/flexstr)\n\n\nA flexible, simple to use, immutable, clone-efficient `String` replacement for \nRust. It unifies literals, inlined, and heap allocated strings into a single \ntype.\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Example](#example)\n- [Installation](#installation)\n- [How Does It Work?](#how-does-it-work)\n- [Features](#features)\n- [Types](#types)\n- [Usage](#usage)\n    - [Hello World!](#hello-world)\n    - [Creation Scenarios](#creation-scenarios)\n    - [Passing FlexStr to Conditional Ownership Functions](#passing-flexstr-to-conditional-ownership-functions)\n    - [Make Your Own String Type](#make-your-own-string-type)\n- [Performance Characteristics](#performance-characteristics)\n- [Benchmarks](#benchmarks)\n- [Downsides](#downsides)\n- [Status](#status)\n- [License](#license)\n\n## Overview\n\nRust is great, but it's `String` type is optimized as a mutable string \nbuffer, not for typical string use cases. Most string use cases don't \nmodify their contents, often need to copy strings around as if \nthey were cheap like integers, typically concatenate instead of modify, and \noften end up being cloned with identical contents. Additionally, `String` \nisn't able to wrap a string literal without additional allocation and \ncopying forcing a choice between efficiency and storing two different types.\n\nI believe Rust needs a new string type to unify usage of both literals and \nallocated strings for typical string use cases. This crate includes a new \nstring type that is optimized for those use cases, while retaining the usage simplicity of\n`String`.\n\n## Example\n\nString constants are easily wrapped into the unified string type. String\ndata is automatically inlined when possible otherwise allocated on the heap.\n\nSee [documentation](https://docs.rs/flexstr) or [Usage](#usage) section for \nmore examples.\n\n```rust\nuse flexstr::{local_str, LocalStr, ToLocalStr};\n\nfn main() {\n  // Use `local_str` macro to wrap literals as compile-time constants\n  const STATIC_STR: LocalStr = local_str!(\"This will not allocate or copy\");\n  assert!(STATIC_STR.is_static());\n\n  // Strings up to 22 bytes (on 64-bit) will be inlined automatically\n  // (demo only, use macro or `from_static` for literals as above)\n  let inline_str = \"inlined\".to_local_str();\n  assert!(inline_str.is_inline());\n\n  // When a string is too long to be wrapped/inlined, it will heap allocate\n  // (demo only, use macro or `from_static` for literals as above)\n  let rc_str = \"This is too long to be inlined\".to_local_str();\n  assert!(rc_str.is_heap());\n}\n```\n\n## Installation\n\nOptional features:\n* `fast_format` = enables `local_ufmt!` and `shared_ufmt!` `format!`-like \n  macros for very fast formatting (with some limitations)\n* `fp_convert` = Convert floating point types directly into a `FlexStr`\n* `int_convert` = Convert integer types directly into a `FlexStr`\n* `serde` = Serialization support for `FlexStr`\n* `std` = enabled by default (use `default-features=false` to enable `#[no_std]`)\n\n```toml\n[dependencies.flexstr]\nversion = \"0.9\"\nfeatures = [\"fast_format\", \"fp_convert\", \"int_convert\", \"serde\"]\n```\n\n## How Does It Work?\n\nInternally, `FlexStr` uses a union with these variants:\n\n* `Static` - A simple wrapper around a static string literal (`\u0026'static str`)\n* `Inline` - An inlined string (no heap allocation for small strings)\n* `Heap` - A heap allocated (reference counted) string\n\nThe type automatically chooses the best storage and allows you to use them \ninterchangeably as a single string type.\n\n## Features\n\n* Optimized for immutability and cheap cloning\n* Allows for multiple ownership of the same string memory contents\n* Serves as a universal string type (unifying literals and allocated strings)\n* Doesn't allocate for literals and short strings (64-bit: up to 22 bytes)\n* The same inline size as a `String` (64-bit: 24 bytes)\n* Optional `serde` serialization support (feature = \"serde\")\n* Compatible with embedded systems (supports `#[no_std]`)\n* Efficient conditional ownership (borrows can take ownership without \n  allocation/copying)\n* Both single threaded compatible (`LocalStr`) and multi-thread safe \n  (`SharedStr`) options\n* All dependencies are optional and based on feature usage\n* It is simple to use!\n\n## Types\n\nNOTE: Both types are identical in handling both literals and inline strings.\nThe only difference occurs when a heap allocation is required.\n\n* `LocalStr` - ultra-fast usage in the local thread\n    * `Heap` storage based on `Rc`\n* `SharedStr`- provides `Send` / `Sync` for multithreaded use\n    * `Heap` storage based on `Arc` \n\n## Usage\n\n### Hello World\n\n```rust\nuse flexstr::local_str;\n\nfn main() {\n  // From literal - no copying or allocation\n  let world = local_str!(\"world!\");\n\n  println!(\"Hello {world}\");\n}\n```\n\n### Creation Scenarios\n\n```rust\nuse flexstr::{local_str, LocalStr, IntoSharedStr, IntoLocalStr, ToLocalStr};\n\nfn main() {\n  // From literal - no runtime, all compile-time\n  const literal: LocalStr = local_str!(\"literal\");\n\n  // From borrowed string - Copied into inline string\n  let owned = \"inlined\".to_string();\n  let str_to_inlined = owned.to_local_str();\n\n  // From borrowed String - copied into `str` wrapped in `Rc`\n  let owned = \"A bit too long to be inlined!!!\".to_string();\n  let str_to_wrapped = owned.to_local_str();\n\n  // From String - copied into inline string (`String` storage released)\n  let inlined = \"inlined\".to_string().into_local_str();\n\n  // From String - `str` wrapped in `Rc` (`String` storage released)\n  let counted = \"A bit too long to be inlined!!!\".to_string().into_local_str();\n\n  // *** If you want a Send/Sync type you need `SharedStr` instead ***\n\n  // From LocalStr wrapped literal - no copying or allocation\n  let literal2 = literal.into_shared_str();\n\n  // From LocalStr inlined string - no allocation\n  let inlined = inlined.into_shared_str();\n\n  // From LocalStr `Rc` wrapped `str` - copies into `str` wrapped in `Arc`\n  let counted = counted.into_shared_str();\n}\n```\n\n### Passing FlexStr to Conditional Ownership Functions\n\nThis has always been a confusing situation in Rust, but it is easy with \n`FlexStr` since multi ownership is cheap. By passing as `\u0026LocalStr` instead \nof `\u0026str`, you retain the option for very fast multi ownership.\n\n```rust\nuse flexstr::{local_str, IntoLocalStr, LocalStr};\n\nstruct MyStruct {\n  s: LocalStr\n}\n\nimpl MyStruct {\n  fn to_own_or_not_to_own(s: \u0026LocalStr) -\u003e Self {\n    let s = if s == \"own me\" {\n      // Since a wrapped literal, no copy or allocation\n      s.clone()\n    } else {\n      // Wrapped literal - no copy or allocation\n      local_str!(\"own me\")\n    };\n\n    Self { s }\n  }\n}\n\nfn main() {\n  // Wrapped literals - compile time constant\n  const S: LocalStr = local_str!(\"borrow me\");\n  const S2: LocalStr = local_str!(\"own me\");\n\n  let struct1 = MyStruct::to_own_or_not_to_own(\u0026S);\n  let struct2 = MyStruct::to_own_or_not_to_own(\u0026S2);\n\n  assert_eq!(S2, struct1.s);\n  assert_eq!(S2, struct2.s);\n}\n```\n\n### Make Your Own String Type\n\nAll you need to do is pick a storage type. The storage type must implement \n`Deref\u003cTarget = str\u003e`, `From\u003c\u0026str\u003e`, and `Clone`. Pretty much all smart \npointers do this already.\n\n#### NOTE:\n\n\u003e Custom concrete types need to specify a heap type with an exact size of two \n\u003e machine words (16 bytes on 64-bit, and 8 bytes on 32-bit). Any other size \n\u003e parameter will result in a runtime panic error message on string creation.\n\n```rust\nuse flexstr::{FlexStrBase, Repeat, ToFlex};\n\ntype BoxStr = FlexStrBase\u003cBox\u003cstr\u003e\u003e;\n\nfn main() {\n  // Any need for a heap string will now be allocated in a `Box` instead of `Rc`\n  // However, the below uses static and inline storage...because we can!\n  let my_str = BoxStr::from_static(\"cool!\").repeat_n(3);\n  assert_eq!(my_str, \"cool!cool!cool!\");\n}\n```\n\n## Performance Characteristics\n\n* Clones are cheap and never allocate\n    * At minimum, they are just a copy of the union and at max an additional \n      reference count increment\n* Literals are just wrapped when used with `into()` and never copied\n* Calling `into()` on a `String` will result in an inline string (if \n  short) otherwise copied into a `str` wrapped in `Rc`/`Arc` \n  (which will allocate, copy, and then release original `String` storage)\n* `into_local_str()` and `into_shared_str()` are equivalent to calling `into()` \n  on both literals and `String` (they are present primarily for `let` \n  bindings so there is no need to declare a type)\n* `to_local_str()` and `to_shared_str()` are meant for taking ownership of \n  borrowed strings and always copy into either an inline string (for short strings) or \n  an `Rc`/`Arc` wrapped `str` (which will allocate)\n* `to_string` always copies into a new `String`\n* Conversions back and forth between `SharedStr` and `LocalStr` using `into()` \n  are cheap when using wrapped literals or inlined strings\n    * Inlined strings and wrapped literals just create a new union wrapper\n    * Reference counted wrapped strings will always require an allocation \n      and copy for the  new `Rc` or `Arc`\n\n## Benchmarks\n\nIn general, inline/static creates are fast but heap creates are a tiny bit \nslower than `String`. Clones are MUCH faster and don't allocate/copy. Other \noperations (repeat, additions, etc.) tend to be about the same performance, \nbut with some nuance depending on string size.\n\n[Full benchmarks](benchmarks/README.md)\n\n## Downsides\n\nThere is no free lunch:\n\n* Due to usage of `Rc` (or `Arc`), when on-boarding `String` it will need to \n  reallocate and copy\n* Due to the union wrapper, every string operation has the overhead of an extra\n  branching operation\n* Since `LocalStr` is not `Send` or `Sync`, there is a need to consider \n  single-threaded   (`LocalStr`) and multi-threaded (`SharedStr`) use cases and \n  convert accordingly\n\n## Status\n\nThis is currently beta quality and still needs testing. The API may very \npossibly change but semantic versioning will be followed.\n\n## License\n\nThis project is licensed optionally under either:\n\n* Apache License, Version 2.0, (LICENSE-APACHE\n  or https://www.apache.org/licenses/LICENSE-2.0)\n* MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnu11ptr%2Fflexstr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnu11ptr%2Fflexstr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnu11ptr%2Fflexstr/lists"}