Repository: ParkMyCar/compact_str
Pull request #293 (2023-08-30 13:27:23)

Update criterion requirement from 0.4 to 0.5
Updates the requirements on [criterion](https://github.com/bheisler/criterion.rs) to permit the latest version.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/bheisler/criterion.rs/blob/master/CHANGELOG.md">criterion's changelog</a>.</em></p>
<blockquote>
<h2>[0.5.1] - 2023-05-26</h2>
<h3>Fixed</h3>
<ul>
<li>Quick mode (--quick) no longer crashes with measured times over 5 seconds when --noplot is not active</li>
</ul>
<h2>[0.5.0] - 2023-05-23</h2>
<h3>Changed</h3>
<ul>
<li>Replaced lazy_static dependency with once_cell</li>
<li>Improved documentation of the <code>html_reports</code> feature</li>
<li>Replaced atty dependency with is-terminal</li>
<li>MSRV bumped to 1.64</li>
<li>Upgraded clap dependency to v4</li>
<li>Upgraded tempfile dependency to v3.5.0</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Quick mode (<code>--quick</code>) no longer outputs 1ms for measured times over 5 seconds</li>
<li>Documentation updates</li>
</ul>
<h2>[0.4.0] - 2022-09-10</h2>
<h3>Removed</h3>
<ul>
<li>The <code>Criterion::can_plot</code> function has been removed.</li>
<li>The <code>Criterion::bench_function_over_inputs</code> function has been removed.</li>
<li>The <code>Criterion::bench_functions</code> function has been removed.</li>
<li>The <code>Criterion::bench</code> function has been removed.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>HTML report hidden behind non-default feature flag: 'html_reports'</li>
<li>Standalone support (ie without cargo-criterion) feature flag: 'cargo_bench_support'</li>
<li>MSRV bumped to 1.57</li>
<li><code>rayon</code> and <code>plotters</code> are optional (and default) dependencies.</li>
<li>Status messages ('warming up', 'analyzing', etc) are printed to stderr, benchmark results are printed to stdout.</li>
<li>Accept subsecond durations for <code>--warm-up-time</code>, <code>--measurement-time</code> and <code>--profile-time</code>.</li>
<li>Replaced serde_cbor with ciborium because the former is no longer maintained.</li>
<li>Upgrade clap to v3 and regex to v1.5.</li>
</ul>
<h3>Added</h3>
<ul>
<li>A <code>--discard-baseline</code> flag for discarding rather than saving benchmark results.</li>
<li>Formal support for benchmarking code compiled to web-assembly.</li>
<li>A <code>--quiet</code> flag for printing just a single line per benchmark.</li>
<li>A <code>Throughput::BytesDecimal</code> option for measuring throughput in bytes but printing them using
decimal units like kilobytes instead of binary units like kibibytes.</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>When using <code>bench_with_input</code>, the input parameter will now be passed through <code>black_box</code> before
passing it to the benchmark.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/bheisler/criterion.rs/commit/dc2b06cd31f7aa34cff6a83a00598e0523186dad"><code>dc2b06c</code></a> Release version 0.5.1 (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/687">#687</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/bfc84ada36a6bba71eae880f787662d4bbde6260"><code>bfc84ad</code></a> fix &gt;5s quick mode crash, resolve <a href="https://redirect.github.com/bheisler/criterion.rs/issues/658">#658</a> (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/685">#685</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/4a560cb8d8b4cefc4819a6489f0ce0540663968d"><code>4a560cb</code></a> release version 0.5 (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/683">#683</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/3e2849ebf86e27507f783a20647a0fc81c6bfcc7"><code>3e2849e</code></a> Post v0.4 CHANGELOG updates (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/680">#680</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/637010e50d3819839cd45d9f5b281c169573416a"><code>637010e</code></a> Bump clap to version 4 (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/679">#679</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/39d7e2f9546981d87ae195fe7c68f778afdb66a4"><code>39d7e2f</code></a> Update to tempfile 3.5 (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/675">#675</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/8ab2752ccfeece50347c81866a4185f29fd978a2"><code>8ab2752</code></a> Fix typo in known_limitations.md (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/674">#674</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/a2112308ef8e3d8fe5a8da41ccb0fba3d5d460c4"><code>a211230</code></a> chore: replace atty with is-terminal (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/628">#628</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/a844eb226bb814e6a4e8b35b3b91c74f22ad535b"><code>a844eb2</code></a> Bump MSRV to 1.60 for csv dependency (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/665">#665</a>)</li>
<li><a href="https://github.com/bheisler/criterion.rs/commit/2f5360737807cbe90d149db6199783236f0ef634"><code>2f53607</code></a> Use OR operator to specify multiple licenses (<a href="https://redirect.github.com/bheisler/criterion.rs/issues/643">#643</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/bheisler/criterion.rs/compare/0.4.0...0.5.1">compare view</a></li>
</ul>
</details>
<br />


You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.



Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 51d992e8..82b3dc50 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -150,9 +150,12 @@ jobs:
         run: |
           cargo test --all-features --manifest-path=compact_str/Cargo.toml
 
-  example-bytes:
-    name: example - bytes
+  examples:
+    name: example
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        ex: ["bytes", "macros", "serde", "traits"] 
     steps:
       - uses: actions/checkout@v3
       - uses: actions-rs/toolchain@v1
@@ -168,93 +171,11 @@ jobs:
             ~/.cargo/registry/cache/
             ~/.cargo/git/db/
             target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example-bytes
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example-${{ matrix.ex }}
           restore-keys: |
             ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example
             ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly
       - uses: actions-rs/cargo@v1
         with:
           command: run
-          args: --manifest-path examples/bytes/Cargo.toml
-
-  example-macros:
-    name: example - macros
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: nightly
-          override: true
-      - uses: actions/cache@v3
-        with:
-          path: |
-            ~/.cargo/bin/
-            ~/.cargo/registry/index/
-            ~/.cargo/registry/cache/
-            ~/.cargo/git/db/
-            target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example-macros
-          restore-keys: |
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly
-      - uses: actions-rs/cargo@v1
-        with:
-          command: run
-          args: --manifest-path examples/macros/Cargo.toml
-
-  example-serde:
-    name: example - serde
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: nightly
-          override: true
-      - uses: actions/cache@v3
-        with:
-          path: |
-            ~/.cargo/bin/
-            ~/.cargo/registry/index/
-            ~/.cargo/registry/cache/
-            ~/.cargo/git/db/
-            target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example-serde
-          restore-keys: |
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly
-      - uses: actions-rs/cargo@v1
-        with:
-          command: run
-          args: --manifest-path examples/serde/Cargo.toml
-  
-  example-traits:
-    name: example - traits
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-      - uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: nightly
-          override: true
-      - uses: actions/cache@v3
-        with:
-          path: |
-            ~/.cargo/bin/
-            ~/.cargo/registry/index/
-            ~/.cargo/registry/cache/
-            ~/.cargo/git/db/
-            target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example-traits
-          restore-keys: |
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly-example
-            ${{ runner.os }}-${{ hashFiles('**/Cargo.toml') }}-nightly
-      - uses: actions-rs/cargo@v1
-        with:
-          command: run
-          args: --manifest-path examples/traits/Cargo.toml
-
+          args: --manifest-path examples/${{ matrix.ex }}/Cargo.toml
diff --git a/.github/workflows/cross_platform.yml b/.github/workflows/cross_platform.yml
index f6829eae..8735be7e 100644
--- a/.github/workflows/cross_platform.yml
+++ b/.github/workflows/cross_platform.yml
@@ -20,9 +20,12 @@ env:
   MIRIFAGS: "-Zmiri-tag-raw-pointers -Zmiri-check-number-validity"
 
 jobs:
-  windows:
-    name: Windows
-    runs-on: windows-latest
+  x_plat_os:
+    name: x_plat_os
+    strategy:
+      matrix:
+        os: [windows-latest, macos-latest]
+    runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v3
         name: Checkout Repo
@@ -41,7 +44,7 @@ jobs:
             ~/.cargo/registry/cache/
             ~/.cargo/git/db/
             target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-windows-v3
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-${{ matrix.os }}
       - uses: actions-rs/cargo@v1
         name: cargo test
         with:
@@ -53,9 +56,25 @@ jobs:
           command: miri
           args: test --all-features --manifest-path=compact_str/Cargo.toml
 
-  macos:
-    name: macOS
-    runs-on: macos-latest
+  x_plat_arch:
+    name: x_plat_arch
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        target: [
+          # 32-bit Little Endian
+          "i686-unknown-linux-gnu",
+          "arm-unknown-linux-gnueabihf",
+          "armv7-unknown-linux-gnueabihf",
+          # 32-bit Big Endian
+          "powerpc-unknown-linux-gnu",
+          # 64-bit Little Endian
+          "powerpc64le-unknown-linux-gnu",
+          "aarch64-unknown-linux-gnu",
+          # 64-bit Big Endian
+          "powerpc64-unknown-linux-gnu",
+        ]
     steps:
       - uses: actions/checkout@v3
         name: Checkout Repo
@@ -64,6 +83,7 @@ jobs:
         with:
           profile: minimal
           toolchain: nightly
+          target: ${{ matrix.target }}
           override: true
           components: miri
       - uses: actions/cache@v3
@@ -74,186 +94,15 @@ jobs:
             ~/.cargo/registry/cache/
             ~/.cargo/git/db/
             target/
-          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-macos-v3
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-${{ matrix.target }}
       - uses: actions-rs/cargo@v1
         name: cargo test
         with:
+          use-cross: true
           command: test
-          args: --release --all-features --manifest-path=compact_str/Cargo.toml -- --include-ignored
+          args: --release --all-features --manifest-path=compact_str/Cargo.toml --target ${{ matrix.target }}
       - uses: actions-rs/cargo@v1
         name: cargo test miri
         with:
           command: miri
-          args: test --all-features --manifest-path=compact_str/Cargo.toml
-
-  # TODO(parkmycar): Re-enable before release of next version.
-  #
-  # linux_32bit:
-  #   name: Linux 32-bit
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - uses: actions/checkout@v3
-  #       name: Checkout Repo
-  #     - uses: actions-rs/toolchain@v1
-  #       name: Install Rust
-  #       with:
-  #         toolchain: nightly
-  #         target: i686-unknown-linux-gnu
-  #         override: true
-  #         components: miri
-  #     - uses: actions/cache@v3
-  #       with:
-  #         path: |
-  #           ~/.cargo/bin/
-  #           ~/.cargo/registry/index/
-  #           ~/.cargo/registry/cache/
-  #           ~/.cargo/git/db/
-  #           target/
-  #         key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-linux-32bit-v2
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test
-  #       with:
-  #         use-cross: true
-  #         command: test
-  #         args: --release --all-features --manifest-path=compact_str/Cargo.toml --target i686-unknown-linux-gnu
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test miri
-  #       with:
-  #         command: miri
-  #         args: test --all-features --manifest-path=compact_str/Cargo.toml --target i686-unknown-linux-gnu
-
-  # linux_mips_32bit:
-  #   name: Linux MIPS Big Endian 32-bit
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - uses: actions/checkout@v3
-  #       name: Checkout Repo
-  #     - uses: actions-rs/toolchain@v1
-  #       name: Install Rust
-  #       with:
-  #         toolchain: nightly
-  #         target: mips-unknown-linux-gnu
-  #         override: true
-  #         components: miri
-  #     - uses: actions/cache@v3
-  #       with:
-  #         path: |
-  #           ~/.cargo/bin/
-  #           ~/.cargo/registry/index/
-  #           ~/.cargo/registry/cache/
-  #           ~/.cargo/git/db/
-  #           target/
-  #         key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-linux-mips-32bit-v2
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test
-  #       with:
-  #         use-cross: true
-  #         command: test
-  #         args: --release --all-features --manifest-path=compact_str/Cargo.toml --target mips-unknown-linux-gnu
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test miri
-  #       with:
-  #         command: miri
-  #         args: test --all-features --manifest-path=compact_str/Cargo.toml --target mips-unknown-linux-gnu
-
-  # linux_mips_le_32bit:
-  #   name: Linux MIPS Little Endian 32-bit
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - uses: actions/checkout@v3
-  #       name: Checkout Repo
-  #     - uses: actions-rs/toolchain@v1
-  #       name: Install Rust
-  #       with:
-  #         toolchain: nightly
-  #         target: mipsel-unknown-linux-gnu
-  #         override: true
-  #         components: miri
-  #     - uses: actions/cache@v3
-  #       with:
-  #         path: |
-  #           ~/.cargo/bin/
-  #           ~/.cargo/registry/index/
-  #           ~/.cargo/registry/cache/
-  #           ~/.cargo/git/db/
-  #           target/
-  #         key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-linux-mips-le-32bit-v2
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test
-  #       with:
-  #         use-cross: true
-  #         command: test
-  #         args: --release --all-features --manifest-path=compact_str/Cargo.toml --target mipsel-unknown-linux-gnu
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test miri
-  #       with:
-  #         command: miri
-  #         args: test --all-features --manifest-path=compact_str/Cargo.toml --target mipsel-unknown-linux-gnu
-
-  # linux_powerpc_bit:
-  #   name: Linux PowerPC Big Endian 32-bit
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - uses: actions/checkout@v3
-  #       name: Checkout Repo
-  #     - uses: actions-rs/toolchain@v1
-  #       name: Install Rust
-  #       with:
-  #         toolchain: nightly
-  #         target: powerpc-unknown-linux-gnu
-  #         override: true
-  #         components: miri
-  #     - uses: actions/cache@v3
-  #       with:
-  #         path: |
-  #           ~/.cargo/bin/
-  #           ~/.cargo/registry/index/
-  #           ~/.cargo/registry/cache/
-  #           ~/.cargo/git/db/
-  #           target/
-  #         key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-linux-powerpc-32bit-v2
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test
-  #       with:
-  #         use-cross: true
-  #         command: test
-  #         args: --release --all-features --manifest-path=compact_str/Cargo.toml --target powerpc-unknown-linux-gnu
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test miri
-  #       with:
-  #         command: miri
-  #         args: test --all-features --manifest-path=compact_str/Cargo.toml --target powerpc-unknown-linux-gnu
-
-  # linux_powerpc_le_64bit:
-  #   name: Linux PowerPC Little Endian 64-bit
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #     - uses: actions/checkout@v3
-  #       name: Checkout Repo
-  #     - uses: actions-rs/toolchain@v1
-  #       name: Install Rust
-  #       with:
-  #         toolchain: nightly
-  #         target: powerpc64le-unknown-linux-gnu
-  #         override: true
-  #         components: miri
-  #     - uses: actions/cache@v3
-  #       with:
-  #         path: |
-  #           ~/.cargo/bin/
-  #           ~/.cargo/registry/index/
-  #           ~/.cargo/registry/cache/
-  #           ~/.cargo/git/db/
-  #           target/
-  #         key: ${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-x-plat-linux-powerpc-le-64bit-v2
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test
-  #       with:
-  #         use-cross: true
-  #         command: test
-  #         args: --release --all-features --manifest-path=compact_str/Cargo.toml --target powerpc64le-unknown-linux-gnu
-  #     - uses: actions-rs/cargo@v1
-  #       name: cargo test miri
-  #       with:
-  #         command: miri
-  #         args: test --all-features --manifest-path=compact_str/Cargo.toml --target powerpc64le-unknown-linux-gnu
+          args: test --all-features --manifest-path=compact_str/Cargo.toml --target ${{ matrix.target }}
diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml
index e863b14d..14db4bc5 100644
--- a/.github/workflows/msrv.yml
+++ b/.github/workflows/msrv.yml
@@ -18,7 +18,7 @@ env:
 
 jobs:
   msrv:
-    name: cargo test msrv..
+    name: cargo check msrv 1.59..
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v3
@@ -59,6 +59,7 @@ jobs:
         # deps decide the version since the API should still be semver compatible
         run: |
           cargo hack check --features bytes,markup,quickcheck,rkyv,serde,smallvec --manifest-path=compact_str/Cargo.toml --version-range 1.59..
+          cargo hack check --features bytes,markup,quickcheck,rkyv,serde,smallvec,proptest,arbitrary --manifest-path=compact_str/Cargo.toml --version-range 1.64..
 
   feature_powerset:
     name: cargo check feature-powerset
diff --git a/.github/workflows/nostd.yml b/.github/workflows/nostd.yml
new file mode 100644
index 00000000..201e7735
--- /dev/null
+++ b/.github/workflows/nostd.yml
@@ -0,0 +1,28 @@
+name: no-std
+
+on:
+  push:
+    branches:
+      - main
+  pull_request:
+  workflow_dispatch:
+
+jobs:
+  no-std:
+    name: no-std build
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        opt: ["", "--release"]
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: stable
+          override: true
+      - uses: actions-rs/cargo@v1
+        with:
+          use-cross: true
+          command: build
+          args: --lib --manifest-path compact_str/Cargo.toml --no-default-features ${{ matrix.opt }} --target thumbv6m-none-eabi
diff --git a/Cargo.toml b/Cargo.toml
index 7f4c2204..4c3bea8e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
     "examples/macros",
     "examples/serde",
     "examples/traits",
-    "compact_str",
     "fuzz",
 ]
+
+exclude = ["compact_str"]
diff --git a/README.md b/README.md
index 0daa595f..561eb819 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,8 @@ Specifically, the last byte on the stack for a `CompactString` has the following
 * `[0, 191]` - Is the last byte of a UTF-8 char, the `CompactString` is stored on the stack and implicitly has a length of `24`
 * `[192, 215]` - Denotes a length in the range of `[0, 23]`, this `CompactString` is stored on the stack.
 * `216` - Denotes this `CompactString` is stored on the heap
-* `[217, 255]` - Unused, denotes e.g. the `None` variant for `Option<CompactString>`
+* `217` - Denotes this `CompactString` stores a `&'static str`.
+* `[218, 255]` - Unused, denotes e.g. the `None` variant for `Option<CompactString>`
 
 ### Testing
 Strings and unicode can be quite messy, even further, we're working with things at the bit level. `compact_str` has an _extensive_ test suite comprised of unit testing, property testing, and fuzz testing, to ensure our invariants are upheld. We test across all major OSes (Windows, macOS, and Linux), architectures (64-bit and 32-bit), and endian-ness (big endian and little endian).
diff --git a/bench/Cargo.toml b/bench/Cargo.toml
index 488a03d9..63177247 100644
--- a/bench/Cargo.toml
+++ b/bench/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 [dependencies]
 compact_str = { path = "../compact_str" }
 compact_str_6 = { package = "compact_str", version = "0.6" }
-criterion = { version = "0.4", default-features = false }
+criterion = { version = "0.5", default-features = false }
 iai = "0.1.1"
 smartstring = "1.0.1"
 smol_str = "0.2"
diff --git a/compact_str/Cargo.toml b/compact_str/Cargo.toml
index c4c8e898..4bfae67f 100644
--- a/compact_str/Cargo.toml
+++ b/compact_str/Cargo.toml
@@ -11,6 +11,12 @@ readme = "../README.md"
 keywords = ["string", "compact", "small", "memory", "mutable"]
 categories = ["encoding", "parsing", "memory-management", "text-processing"]
 
+[features]
+default = [
+    "std",
+]
+std = []
+
 [dependencies]
 arbitrary = { version = "1", optional = true, default-features = false }
 bytes = { version = "1", optional = true }
@@ -21,23 +27,27 @@ rkyv = { version = "0.7", optional = true, default-features = false, features =
 serde = { version = "1", optional = true }
 smallvec = { version = "1", optional = true, features = ["union"] }
 
-castaway = "0.2"
+# TODO: replace with the below version when castaway is updated on crates.io
+castaway = { version = "0.2", default-features = false }
+# castaway = { version = "0.2", default-features = false, features = ["alloc"] }
+
 cfg-if = "1"
 itoa = "1"
+rustversion = "1"
 ryu = "1"
 static_assertions = "1"
 
 [dev-dependencies]
 cfg-if = "1"
-proptest = { version = "1.0.*", default-features = false, features = ["std"] }
+proptest = { version = "1", default-features = false, features = ["std"] }
 quickcheck = { version = "1", default-features = false }
 quickcheck_macros = "1"
-rayon = "1.6.0"
+rayon = "1"
 rkyv = { version = "0.7", default-features = false, features = ["alloc", "size_32"] }
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
 test-case = "3"
-test-strategy = "0.2"
+test-strategy = "0.3"
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/compact_str/src/features/bytes.rs b/compact_str/src/features/bytes.rs
index 617a5a0a..a26b1a7a 100644
--- a/compact_str/src/features/bytes.rs
+++ b/compact_str/src/features/bytes.rs
@@ -69,6 +69,9 @@ impl CompactString {
 
 #[cfg(test)]
 mod test {
+    use alloc::string::String;
+    use alloc::vec::Vec;
+    #[cfg(feature = "std")]
     use std::io::Cursor;
 
     use proptest::prelude::*;
diff --git a/compact_str/src/features/markup.rs b/compact_str/src/features/markup.rs
index 9d3fdc1c..0fd37356 100644
--- a/compact_str/src/features/markup.rs
+++ b/compact_str/src/features/markup.rs
@@ -1,4 +1,5 @@
-use std::fmt;
+#[cfg(test)]
+use alloc::string::String;
 
 use markup::Render;
 
@@ -7,7 +8,7 @@ use crate::CompactString;
 #[cfg_attr(docsrs, doc(cfg(feature = "markup")))]
 impl Render for CompactString {
     #[inline]
-    fn render(&self, writer: &mut impl fmt::Write) -> fmt::Result {
+    fn render(&self, writer: &mut impl core::fmt::Write) -> core::fmt::Result {
         self.as_str().render(writer)
     }
 }
diff --git a/compact_str/src/features/quickcheck.rs b/compact_str/src/features/quickcheck.rs
index 286de025..34caa0cf 100644
--- a/compact_str/src/features/quickcheck.rs
+++ b/compact_str/src/features/quickcheck.rs
@@ -1,5 +1,8 @@
 //! Implements the [`quickcheck::Arbitrary`] trait for [`CompactString`]
 
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+
 use quickcheck::{
     Arbitrary,
     Gen,
@@ -33,6 +36,8 @@ impl Arbitrary for CompactString {
 
 #[cfg(test)]
 mod test {
+    use alloc::string::String;
+
     use quickcheck_macros::quickcheck;
 
     use crate::CompactString;
@@ -47,7 +52,7 @@ mod test {
     #[quickcheck]
     #[cfg_attr(miri, ignore)]
     fn quickcheck_inlines_strings(compact: CompactString) {
-        if compact.len() <= std::mem::size_of::<String>() {
+        if compact.len() <= core::mem::size_of::<String>() {
             assert!(!compact.is_heap_allocated())
         } else {
             assert!(compact.is_heap_allocated())
diff --git a/compact_str/src/features/rkyv.rs b/compact_str/src/features/rkyv.rs
index 22cab001..8dd10682 100644
--- a/compact_str/src/features/rkyv.rs
+++ b/compact_str/src/features/rkyv.rs
@@ -54,13 +54,15 @@ impl PartialEq<CompactString> for ArchivedString {
 
 impl PartialOrd<CompactString> for ArchivedString {
     #[inline]
-    fn partial_cmp(&self, other: &CompactString) -> Option<std::cmp::Ordering> {
+    fn partial_cmp(&self, other: &CompactString) -> Option<core::cmp::Ordering> {
         PartialOrd::partial_cmp(self.as_str(), other.as_str())
     }
 }
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::String;
+
     use rkyv::Deserialize;
     use test_strategy::proptest;
 
diff --git a/compact_str/src/features/serde.rs b/compact_str/src/features/serde.rs
index eb2aa2c6..a78dd820 100644
--- a/compact_str/src/features/serde.rs
+++ b/compact_str/src/features/serde.rs
@@ -1,4 +1,5 @@
-use std::fmt;
+use alloc::string::String;
+use alloc::vec::Vec;
 
 use serde::de::{
     Deserializer,
@@ -17,7 +18,7 @@ fn compact_string<'de: 'a, 'a, D: Deserializer<'de>>(
     impl<'a> Visitor<'a> for CompactStringVisitor {
         type Value = CompactString;
 
-        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
             formatter.write_str("a string")
         }
 
@@ -34,14 +35,14 @@ fn compact_string<'de: 'a, 'a, D: Deserializer<'de>>(
         }
 
         fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
-            match std::str::from_utf8(v) {
+            match core::str::from_utf8(v) {
                 Ok(s) => Ok(CompactString::from(s)),
                 Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
             }
         }
 
         fn visit_borrowed_bytes<E: Error>(self, v: &'a [u8]) -> Result<Self::Value, E> {
-            match std::str::from_utf8(v) {
+            match core::str::from_utf8(v) {
                 Ok(s) => Ok(CompactString::from(s)),
                 Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)),
             }
@@ -77,6 +78,12 @@ impl<'de> serde::Deserialize<'de> for CompactString {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::{
+        String,
+        ToString,
+    };
+    use alloc::vec::Vec;
+
     use serde::{
         Deserialize,
         Serialize,
diff --git a/compact_str/src/features/smallvec.rs b/compact_str/src/features/smallvec.rs
index 118ba04d..128b9304 100644
--- a/compact_str/src/features/smallvec.rs
+++ b/compact_str/src/features/smallvec.rs
@@ -9,8 +9,8 @@ impl CompactString {
     /// This consumes the [`CompactString`] and returns a [`SmallVec`], so we do not need to copy
     /// contents
     ///
-    /// Note: [`SmallVec`] is an inline-able version [`Vec`], just like [`CompactString`] is an
-    /// inline-able version of [`String`].
+    /// Note: [`SmallVec`] is an inline-able version [`Vec`](alloc::vec::Vec), just like
+    /// [`CompactString`] is an inline-able version of [`String`](alloc::string::String).
     ///
     /// # Example
     /// ```
@@ -29,6 +29,8 @@ impl CompactString {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::String;
+
     use proptest::prelude::*;
     use test_strategy::proptest;
 
diff --git a/compact_str/src/lib.rs b/compact_str/src/lib.rs
index 170d74a6..e4cd1ea2 100644
--- a/compact_str/src/lib.rs
+++ b/compact_str/src/lib.rs
@@ -1,6 +1,17 @@
 #![doc = include_str!("../README.md")]
 #![cfg_attr(docsrs, feature(doc_cfg))]
+#![no_std]
 
+#[cfg(feature = "std")]
+#[macro_use]
+extern crate std;
+
+#[cfg_attr(test, macro_use)]
+extern crate alloc;
+
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
+use alloc::string::String;
 #[doc(hidden)]
 pub use core;
 use core::borrow::{
@@ -12,7 +23,10 @@ use core::hash::{
     Hash,
     Hasher,
 };
-use core::iter::FromIterator;
+use core::iter::{
+    FromIterator,
+    FusedIterator,
+};
 use core::ops::{
     Add,
     AddAssign,
@@ -30,9 +44,8 @@ use core::{
     mem,
     slice,
 };
-use std::borrow::Cow;
+#[cfg(feature = "std")]
 use std::ffi::OsStr;
-use std::iter::FusedIterator;
 
 mod features;
 mod macros;
@@ -216,6 +229,37 @@ impl CompactString {
         CompactString(Repr::new_inline(text))
     }
 
+    /// Creates a new inline [`CompactString`] from `&'static str`
+    /// at compile time.
+    ///
+    /// Complexity: O(1)
+    ///
+    /// # Examples
+    /// ```
+    /// use compact_str::CompactString;
+    ///
+    /// const DEFAULT_NAME: CompactString = CompactString::from_static_str("untitled");
+    /// ```
+    #[inline]
+    pub const fn from_static_str(text: &'static str) -> Self {
+        CompactString(Repr::from_static_str(text))
+    }
+
+    /// Get back the `&'static str` constructed by [`CompactString::from_static_str`].
+    ///
+    /// # Examples
+    /// ```
+    /// use compact_str::CompactString;
+    ///
+    /// const DEFAULT_NAME: CompactString = CompactString::from_static_str("untitled");
+    /// assert_eq!(DEFAULT_NAME.as_static_str().unwrap(), "untitled");
+    /// ```
+    #[inline]
+    #[rustversion::attr(since(1.64), const)]
+    pub fn as_static_str(&self) -> Option<&'static str> {
+        self.0.as_static_str()
+    }
+
     /// Creates a new empty [`CompactString`] with the capacity to fit at least `capacity` bytes.
     ///
     /// A `CompactString` will inline strings on the stack, if they're small enough. Specifically,
@@ -396,7 +440,7 @@ impl CompactString {
     pub fn from_utf16_lossy<B: AsRef<[u16]>>(buf: B) -> Self {
         let buf = buf.as_ref();
         let mut ret = CompactString::with_capacity(buf.len());
-        for c in std::char::decode_utf16(buf.iter().copied()) {
+        for c in core::char::decode_utf16(buf.iter().copied()) {
             match c {
                 Ok(c) => ret.push(c),
                 Err(_) => ret.push_str("�"),
@@ -523,7 +567,7 @@ impl CompactString {
     #[inline]
     pub fn as_mut_str(&mut self) -> &mut str {
         let len = self.len();
-        unsafe { std::str::from_utf8_unchecked_mut(&mut self.0.as_mut_buf()[..len]) }
+        unsafe { core::str::from_utf8_unchecked_mut(&mut self.0.as_mut_buf()[..len]) }
     }
 
     unsafe fn spare_capacity_mut(&mut self) -> &mut [mem::MaybeUninit<u8>] {
@@ -960,14 +1004,14 @@ impl CompactString {
         unsafe {
             // first move the tail to the new back
             let data = self.as_mut_ptr();
-            std::ptr::copy(
+            core::ptr::copy(
                 data.add(idx),
                 data.add(idx + string.len()),
                 new_len - idx - string.len(),
             );
 
             // then insert the new bytes
-            std::ptr::copy_nonoverlapping(string.as_ptr(), data.add(idx), string.len());
+            core::ptr::copy_nonoverlapping(string.as_ptr(), data.add(idx), string.len());
 
             // and lastly resize the string
             self.set_len(new_len);
@@ -1020,15 +1064,22 @@ impl CompactString {
     ///
     /// ```
     /// # use compact_str::CompactString;
-    /// let mut s = CompactString::new("Hello, world!");
+    /// let mut s = CompactString::from_static_str("Hello, world!");
     /// assert_eq!(s.split_off(5), ", world!");
     /// assert_eq!(s, "Hello");
     /// ```
     pub fn split_off(&mut self, at: usize) -> Self {
-        let result = self[at..].into();
-        // SAFETY: the previous line `self[at...]` would have panicked if `at` was invalid
-        unsafe { self.set_len(at) };
-        result
+        if let Some(s) = self.as_static_str() {
+            let result = Self::from_static_str(&s[at..]);
+            // SAFETY: the previous line `self[at...]` would have panicked if `at` was invalid
+            unsafe { self.set_len(at) };
+            result
+        } else {
+            let result = self[at..].into();
+            // SAFETY: the previous line `self[at...]` would have panicked if `at` was invalid
+            unsafe { self.set_len(at) };
+            result
+        }
     }
 
     /// Remove a range from the [`CompactString`], and return it as an iterator.
@@ -1292,7 +1343,7 @@ impl CompactString {
         let mut iter = v.iter();
         while let Some(s) = next_char(&mut iter, &mut buf) {
             // SAFETY: next_char() only returns valid strings
-            let s = unsafe { std::str::from_utf8_unchecked(s) };
+            let s = unsafe { core::str::from_utf8_unchecked(s) };
             result.push_str(s);
         }
         result
@@ -1319,7 +1370,7 @@ impl CompactString {
         match unsafe { v.align_to::<u16>() } {
             (&[], v, &[]) => {
                 // Input is correcty aligned.
-                for c in std::char::decode_utf16(v.iter().copied().map(from_int)) {
+                for c in core::char::decode_utf16(v.iter().copied().map(from_int)) {
                     result.push(c.map_err(|_| Utf16Error(()))?);
                 }
             }
@@ -1327,7 +1378,7 @@ impl CompactString {
                 // Input's alignment is off.
                 // SAFETY: we can always reinterpret a `[u8; 2*N]` slice as `[[u8; 2]; N]`
                 let v = unsafe { slice::from_raw_parts(v.as_ptr().cast(), v.len() / 2) };
-                for c in std::char::decode_utf16(v.iter().copied().map(from_bytes)) {
+                for c in core::char::decode_utf16(v.iter().copied().map(from_bytes)) {
                     result.push(c.map_err(|_| Utf16Error(()))?);
                 }
             }
@@ -1355,7 +1406,7 @@ impl CompactString {
         match unsafe { v.align_to::<u16>() } {
             (&[], v, &[]) => {
                 // Input is correcty aligned.
-                for c in std::char::decode_utf16(v.iter().copied().map(from_int)) {
+                for c in core::char::decode_utf16(v.iter().copied().map(from_int)) {
                     match c {
                         Ok(c) => result.push(c),
                         Err(_) => result.push_str("�"),
@@ -1366,7 +1417,7 @@ impl CompactString {
                 // Input's alignment is off.
                 // SAFETY: we can always reinterpret a `[u8; 2*N]` slice as `[[u8; 2]; N]`
                 let v = unsafe { slice::from_raw_parts(v.as_ptr().cast(), v.len() / 2) };
-                for c in std::char::decode_utf16(v.iter().copied().map(from_bytes)) {
+                for c in core::char::decode_utf16(v.iter().copied().map(from_bytes)) {
                     match c {
                         Ok(c) => result.push(c),
                         Err(_) => result.push_str("�"),
@@ -1904,6 +1955,7 @@ impl AsRef<str> for CompactString {
     }
 }
 
+#[cfg(feature = "std")]
 impl AsRef<OsStr> for CompactString {
     #[inline]
     fn as_ref(&self) -> &OsStr {
@@ -2024,7 +2076,11 @@ impl From<CompactString> for String {
 impl From<CompactString> for Cow<'_, str> {
     #[inline]
     fn from(s: CompactString) -> Self {
-        Self::Owned(s.into_string())
+        if let Some(s) = s.as_static_str() {
+            Self::Borrowed(s)
+        } else {
+            Self::Owned(s.into_string())
+        }
     }
 }
 
@@ -2190,7 +2246,15 @@ impl fmt::Write for CompactString {
     fn write_fmt(mut self: &mut Self, args: fmt::Arguments<'_>) -> fmt::Result {
         match args.as_str() {
             Some(s) => {
-                self.push_str(s);
+                if self.is_empty() && !self.is_heap_allocated() {
+                    // Since self is currently an empty inline variant or
+                    // an empty `StaticStr` variant, constructing a new one
+                    // with `Self::from_static_str` is more efficient since
+                    // it is guaranteed to be O(1).
+                    *self = Self::from_static_str(s);
+                } else {
+                    self.push_str(s);
+                }
                 Ok(())
             }
             None => fmt::write(&mut self, args),
@@ -2244,7 +2308,7 @@ pub struct Drain<'a> {
     compact_string: *mut CompactString,
     start: usize,
     end: usize,
-    chars: std::str::Chars<'a>,
+    chars: core::str::Chars<'a>,
 }
 
 // SAFETY: Drain keeps the lifetime of the CompactString it belongs to.
diff --git a/compact_str/src/repr/bytes.rs b/compact_str/src/repr/bytes.rs
index 0bab577f..798baeaf 100644
--- a/compact_str/src/repr/bytes.rs
+++ b/compact_str/src/repr/bytes.rs
@@ -81,6 +81,7 @@ impl Repr {
 
 #[cfg(test)]
 mod test {
+    #[cfg(feature = "std")]
     use std::io::Cursor;
 
     use test_case::test_case;
@@ -156,7 +157,7 @@ mod test {
             51, 51, 0, 52, 55, 247, 204, 45, 44, 210, 132, 50, 206, 51,
         ];
         let (front, back) = data.split_at(data.len() / 2 + 1);
-        let mut queue = std::collections::VecDeque::with_capacity(data.len());
+        let mut queue = alloc::collections::VecDeque::with_capacity(data.len());
 
         // create a non-contiguous slice of memory in queue
         front.into_iter().copied().for_each(|x| queue.push_back(x));
diff --git a/compact_str/src/repr/capacity.rs b/compact_str/src/repr/capacity.rs
index ff3d10df..bab7b416 100644
--- a/compact_str/src/repr/capacity.rs
+++ b/compact_str/src/repr/capacity.rs
@@ -107,8 +107,6 @@ impl Capacity {
 
 #[cfg(test)]
 mod tests {
-    use rayon::prelude::*;
-
     use super::Capacity;
 
     #[test]
@@ -147,9 +145,12 @@ mod tests {
         assert_eq!(16777215, after);
     }
 
+    #[rustversion::since(1.63)]
     #[test]
     #[cfg_attr(miri, ignore)]
     fn test_all_valid_32bit_values() {
+        use rayon::prelude::*;
+
         #[cfg(target_pointer_width = "32")]
         assert_eq!(16_777_214, super::MAX_VALUE);
 
diff --git a/compact_str/src/repr/heap.rs b/compact_str/src/repr/heap.rs
index 31054dc9..04001bbe 100644
--- a/compact_str/src/repr/heap.rs
+++ b/compact_str/src/repr/heap.rs
@@ -148,7 +148,7 @@ impl HeapBuffer {
                 // * `new_size` will be > 0, we return early if the requested capacity is 0
                 // * Checked above if `new_size` overflowed when rounding to alignment
                 match ptr::NonNull::new(unsafe {
-                    std::alloc::realloc(self.ptr.as_ptr(), cur_layout, new_size)
+                    ::alloc::alloc::realloc(self.ptr.as_ptr(), cur_layout, new_size)
                 }) {
                     Some(ptr) => (new_cap, ptr),
                     None => return Err(()),
@@ -178,7 +178,7 @@ impl HeapBuffer {
                 // * The layout is the same because we checked that the capacity is on the heap
                 // * `new_size` will be > 0, we return early if the requested capacity is 0
                 // * Checked above if `new_size` overflowed when rounding to alignment
-                let cap_ptr = unsafe { std::alloc::realloc(adj_ptr, cur_layout, new_size) };
+                let cap_ptr = unsafe { alloc::alloc::realloc(adj_ptr, cur_layout, new_size) };
                 // Check if reallocation succeeded
                 if cap_ptr.is_null() {
                     return Err(());
@@ -331,8 +331,10 @@ pub fn deallocate_ptr(ptr: ptr::NonNull<u8>, cap: Capacity) {
 }
 
 mod heap_capacity {
-    use core::ptr;
-    use std::alloc;
+    use core::{
+        alloc,
+        ptr,
+    };
 
     use super::StrBuffer;
 
@@ -343,13 +345,13 @@ mod heap_capacity {
 
         // SAFETY: `alloc(...)` has undefined behavior if the layout is zero-sized. We know the
         // layout can't be zero-sized though because we're always at least allocating one `usize`
-        let raw_ptr = unsafe { alloc::alloc(layout) };
+        let raw_ptr = unsafe { ::alloc::alloc::alloc(layout) };
 
         // Check to make sure our pointer is non-null, some allocators return null pointers instead
         // of panicking
         match ptr::NonNull::new(raw_ptr) {
             Some(ptr) => ptr,
-            None => alloc::handle_alloc_error(layout),
+            None => ::alloc::alloc::handle_alloc_error(layout),
         }
     }
 
@@ -360,7 +362,7 @@ mod heap_capacity {
     ///   must have `ptr -> [cap<usize> ; string<bytes>]`
     pub unsafe fn dealloc(ptr: ptr::NonNull<u8>, capacity: usize) {
         let layout = layout(capacity);
-        alloc::dealloc(ptr.as_ptr(), layout);
+        ::alloc::alloc::dealloc(ptr.as_ptr(), layout);
     }
 
     #[repr(C)]
@@ -381,8 +383,10 @@ mod heap_capacity {
 }
 
 mod inline_capacity {
-    use core::ptr;
-    use std::alloc;
+    use core::{
+        alloc,
+        ptr,
+    };
 
     use super::StrBuffer;
 
@@ -396,13 +400,13 @@ mod inline_capacity {
         // SAFETY: `alloc(...)` has undefined behavior if the layout is zero-sized. We specify that
         // `capacity` must be > 0 as a constraint to uphold the safety of this method. If capacity
         // is greater than 0, then our layout will be non-zero-sized.
-        let raw_ptr = alloc::alloc(layout);
+        let raw_ptr = ::alloc::alloc::alloc(layout);
 
         // Check to make sure our pointer is non-null, some allocators return null pointers instead
         // of panicking
         match ptr::NonNull::new(raw_ptr) {
             Some(ptr) => ptr,
-            None => alloc::handle_alloc_error(layout),
+            None => ::alloc::alloc::handle_alloc_error(layout),
         }
     }
 
@@ -412,7 +416,7 @@ mod inline_capacity {
     /// * `ptr` must point to the start of a `HeapBuffer` whose capacity is on the inline
     pub unsafe fn dealloc(ptr: ptr::NonNull<u8>, capacity: usize) {
         let layout = layout(capacity);
-        alloc::dealloc(ptr.as_ptr(), layout);
+        ::alloc::alloc::dealloc(ptr.as_ptr(), layout);
     }
 
     #[repr(C)]
diff --git a/compact_str/src/repr/inline.rs b/compact_str/src/repr/inline.rs
index 2aab72b9..952e3584 100644
--- a/compact_str/src/repr/inline.rs
+++ b/compact_str/src/repr/inline.rs
@@ -113,20 +113,16 @@ impl InlineBuffer {
             self.0[MAX_SIZE - 1] = len as u8 | LENGTH_MASK;
         }
     }
-
-    #[inline(always)]
-    pub fn copy(&self) -> Self {
-        InlineBuffer(self.0)
-    }
 }
 
 #[cfg(test)]
 mod tests {
-    use rayon::prelude::*;
-
+    #[rustversion::since(1.63)]
     #[test]
     #[ignore] // we run this in CI, but unless you're compiling in release, this takes a while
     fn test_unused_utf8_bytes() {
+        use rayon::prelude::*;
+
         // test to validate for all char the first and last bytes are never within a specified range
         // note: according to the UTF-8 spec it shouldn't be, but we double check that here
         (0..u32::MAX).into_par_iter().for_each(|i| {
@@ -152,6 +148,8 @@ mod tests {
 
     #[cfg(feature = "smallvec")]
     mod smallvec {
+        use alloc::string::String;
+
         use quickcheck_macros::quickcheck;
 
         use crate::repr::{
@@ -172,7 +170,7 @@ mod tests {
             assert!(array[length..].iter().all(|b| *b == 0));
 
             // taking a string slice should give back the same string as the original
-            let ex_s = unsafe { std::str::from_utf8_unchecked(&array[..length]) };
+            let ex_s = unsafe { core::str::from_utf8_unchecked(&array[..length]) };
             assert_eq!(s, ex_s);
         }
 
@@ -196,7 +194,7 @@ mod tests {
             assert!(array[length..].iter().all(|b| *b == 0));
 
             // taking a string slice should give back the same string as the original
-            let ex_s = unsafe { std::str::from_utf8_unchecked(&array[..length]) };
+            let ex_s = unsafe { core::str::from_utf8_unchecked(&array[..length]) };
             assert_eq!(s, ex_s);
         }
     }
diff --git a/compact_str/src/repr/iter.rs b/compact_str/src/repr/iter.rs
index 38ca6bcd..e0f2ebd3 100644
--- a/compact_str/src/repr/iter.rs
+++ b/compact_str/src/repr/iter.rs
@@ -1,7 +1,9 @@
 //! Implementations of the [`FromIterator`] trait to make building [`Repr`]s more ergonomic
 
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
+use alloc::string::String;
 use core::iter::FromIterator;
-use std::borrow::Cow;
 
 use super::{
     InlineBuffer,
@@ -139,6 +141,8 @@ impl<'a> FromIterator<Cow<'a, str>> for Repr {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::String;
+
     use super::Repr;
 
     #[test]
diff --git a/compact_str/src/repr/last_utf8_char.rs b/compact_str/src/repr/last_utf8_char.rs
index 903227a7..fc28aeea 100644
--- a/compact_str/src/repr/last_utf8_char.rs
+++ b/compact_str/src/repr/last_utf8_char.rs
@@ -1,3 +1,5 @@
+use alloc::string::String;
+
 /// [`LastUtf8Char`] is an unsigned 8-bit integer data type that has a valid range of `[0, 216]`.
 /// Excluding `[217, 255]` allows the Rust compiler to use these values as niches.
 ///
@@ -236,7 +238,8 @@ pub enum LastUtf8Char {
     L23 = 215,
 
     Heap = 216,
+    Static = 217,
 }
 
 static_assertions::assert_eq_size!(LastUtf8Char, Option<LastUtf8Char>, u8);
-static_assertions::const_assert!(std::mem::size_of::<String>() <= 24);
+static_assertions::const_assert!(core::mem::size_of::<String>() <= 24);
diff --git a/compact_str/src/repr/mod.rs b/compact_str/src/repr/mod.rs
index d1928fd4..7c4d5ded 100644
--- a/compact_str/src/repr/mod.rs
+++ b/compact_str/src/repr/mod.rs
@@ -1,9 +1,10 @@
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
 use core::str::Utf8Error;
 use core::{
     mem,
     ptr,
 };
-use std::borrow::Cow;
 
 #[cfg(feature = "bytes")]
 mod bytes;
@@ -16,21 +17,27 @@ mod inline;
 mod iter;
 mod last_utf8_char;
 mod num;
+mod static_str;
 mod traits;
 
+use alloc::string::String;
+
 use capacity::Capacity;
 use heap::HeapBuffer;
 use inline::InlineBuffer;
 use last_utf8_char::LastUtf8Char;
+use static_str::StaticStr;
 pub use traits::IntoRepr;
 
 /// The max size of a string we can fit inline
-pub const MAX_SIZE: usize = std::mem::size_of::<String>();
+pub const MAX_SIZE: usize = core::mem::size_of::<String>();
 /// Used as a discriminant to identify different variants
 pub const HEAP_MASK: u8 = LastUtf8Char::Heap as u8;
+/// Used for `StaticStr` variant
+pub const STATIC_STR_MASK: u8 = LastUtf8Char::Static as u8;
 /// When our string is stored inline, we represent the length of the string in the last byte, offset
 /// by `LENGTH_MASK`
-pub const LENGTH_MASK: u8 = LastUtf8Char::L0 as u8;
+pub const LENGTH_MASK: u8 = 0b11000000;
 
 const EMPTY: Repr = Repr::new_inline("");
 
@@ -47,6 +54,7 @@ pub struct Repr(
     // ...so that the last byte can be a NonMax, which allows the compiler to see a niche value
     LastUtf8Char,
 );
+static_assertions::assert_eq_size!([u8; MAX_SIZE], Repr);
 
 unsafe impl Send for Repr {}
 unsafe impl Sync for Repr {}
@@ -76,10 +84,17 @@ impl Repr {
             let inline = InlineBuffer::new_const(text);
             Repr::from_inline(inline)
         } else {
-            panic!("Inline string was too long, max length is `std::mem::size_of::<CompactString>()` bytes");
+            panic!("Inline string was too long, max length is `core::mem::size_of::<CompactString>()` bytes");
         }
     }
 
+    #[inline]
+    pub const fn from_static_str(text: &'static str) -> Self {
+        let repr = StaticStr::new(text);
+        // SAFETY: A `StaticStr` and `Repr` have the same size
+        unsafe { core::mem::transmute(repr) }
+    }
+
     /// Create a [`Repr`] with the provided `capacity`
     #[inline]
     pub fn with_capacity(capacity: usize) -> Self {
@@ -186,8 +201,6 @@ impl Repr {
     /// Converts a [`Repr`] into a [`String`], in `O(1)` time, if possible
     #[inline]
     pub fn into_string(self) -> String {
-        let last_byte = self.last_byte();
-
         #[cold]
         fn into_string_heap(this: HeapBuffer) -> String {
             // SAFETY: We know pointer is valid for `length` bytes
@@ -198,7 +211,7 @@ impl Repr {
             String::from(s)
         }
 
-        if last_byte == HEAP_MASK {
+        if self.is_heap_allocated() {
             // SAFTEY: we just checked that the discriminant indicates we're a HeapBuffer
             let heap_buffer = unsafe { self.into_heap() };
 
@@ -221,15 +234,7 @@ impl Repr {
                 unsafe { String::from_raw_parts(this.ptr.as_ptr(), this.len, cap) }
             }
         } else {
-            let pointer = &self as *const _ as *const u8;
-            let length = core::cmp::min((last_byte.wrapping_sub(LENGTH_MASK)) as usize, MAX_SIZE);
-
-            // SAFETY: We know pointer is valid for `length` bytes
-            let slice = unsafe { core::slice::from_raw_parts(pointer, length) };
-            // SAFETY: A `Repr` contains valid UTF-8
-            let s = unsafe { core::str::from_utf8_unchecked(slice) };
-
-            String::from(s)
+            String::from(self.as_str())
         }
     }
 
@@ -242,8 +247,10 @@ impl Repr {
             .checked_add(additional)
             .expect("Attempted to reserve more than 'usize' bytes");
 
-        if needed_capacity < self.capacity() {
+        if !self.is_static_str() && needed_capacity <= self.capacity() {
             // we already have enough space, no-op
+            // If self.is_static_str() is true, then we would have to convert
+            // it to other variants since static_str variant cannot be modified.
             return;
         }
 
@@ -276,11 +283,10 @@ impl Repr {
     }
 
     pub fn shrink_to(&mut self, min_capacity: usize) {
-        let last_byte = self.last_byte();
-
-        // Note: We can't shrink the inline variant since it's buffer is a fixed size, so we only
+        // Note: We can't shrink the inline variant since it's buffer is a fixed size
+        // or the static str variant since it is just a pointer, so we only
         // take action here if our string is heap allocated
-        if last_byte == HEAP_MASK {
+        if self.is_heap_allocated() {
             // SAFETY: We just checked the discriminant to make sure we're heap allocated
             let heap = unsafe { self.as_mut_heap() };
 
@@ -311,6 +317,12 @@ impl Repr {
 
     #[inline]
     pub fn push_str(&mut self, s: &str) {
+        // If `s` is empty, then there's no reason to reserve or push anything
+        // at all.
+        if s.is_empty() {
+            return;
+        }
+
         let len = self.len();
         let str_len = s.len();
 
@@ -346,23 +358,20 @@ impl Repr {
     /// Returns the string content, and only the string content, as a slice of bytes.
     #[inline]
     pub fn as_slice(&self) -> &[u8] {
-        // the last byte stores our discriminant and stack length
-        let last_byte = self.last_byte();
-
         // initially has the value of the stack pointer, conditionally becomes the heap pointer
         let mut pointer = self as *const Self as *const u8;
         let heap_pointer = self.0 as *const u8;
+        if self.last_byte() >= HEAP_MASK {
+            pointer = heap_pointer;
+        }
 
         // initially has the value of the stack length, conditionally becomes the heap length
-        let mut length = core::cmp::min((last_byte.wrapping_sub(LENGTH_MASK)) as usize, MAX_SIZE);
+        let mut length = core::cmp::min(
+            self.last_byte().wrapping_sub(LENGTH_MASK) as usize,
+            MAX_SIZE,
+        );
         let heap_length = self.1;
-
-        // our discriminant is stored in the last byte and denotes stack vs heap
-        //
-        // Note: We should never add an `else` statement here, keeping the conditional simple allows
-        // the compiler to optimize this to a conditional-move instead of a branch
-        if last_byte == HEAP_MASK {
-            pointer = heap_pointer;
+        if self.last_byte() >= HEAP_MASK {
             length = heap_length;
         }
 
@@ -389,36 +398,47 @@ impl Repr {
         // Force the compiler to read the variable, so it won't put the reading in a branch.
         let len_heap = self.1;
         // SAFETY: This assembly instruction is a noop that only affects the instruction ordering.
-        #[cfg(not(miri))]
+        #[cfg(all(
+            not(miri),
+            any(
+                target_arch = "x86",
+                target_arch = "x86_64",
+                target_arch = "arm",
+                target_arch = "aarch64",
+                target_arch = "loongarch",
+                target_arch = "riscv",
+            )
+        ))]
         unsafe {
-            std::arch::asm!(
+            core::arch::asm!(
                 "/* {len_heap} */",
                 len_heap = in(reg) len_heap,
                 options(nomem, nostack),
             );
         };
 
+        let last_byte = self.last_byte();
+
         // Extending the variable early results in fewer instructions, because loading and
         // extending can be done in one instruction.
-        let len_inline = (self.last_byte() as usize).wrapping_sub(LENGTH_MASK as usize);
+        let mut len = (last_byte as usize)
+            .wrapping_sub(LENGTH_MASK as usize)
+            .min(MAX_SIZE);
 
-        // Do not add an `else` statement, or the compiler will introduce branching.
-        let mut len = len_heap;
-        if len_inline < MAX_SIZE {
-            len = len_inline;
-        }
-        if len_inline > MAX_SIZE {
-            len = MAX_SIZE;
+        // our discriminant is stored in the last byte and denotes stack vs heap
+        //
+        // Note: We should never add an `else` statement here, keeping the conditional simple allows
+        // the compiler to optimize this to a conditional-move instead of a branch
+        if last_byte >= HEAP_MASK {
+            len = len_heap;
         }
+
         len
     }
 
     /// Returns the overall capacity of the underlying buffer
     #[inline]
     pub fn capacity(&self) -> usize {
-        // the last byte stores our discriminant and stack length
-        let last_byte = self.last_byte();
-
         #[cold]
         fn heap_capacity(this: &Repr) -> usize {
             // SAFETY: We just checked the discriminant to make sure we're heap allocated
@@ -426,7 +446,9 @@ impl Repr {
             heap_buffer.capacity()
         }
 
-        if last_byte == HEAP_MASK {
+        if let Some(s) = self.as_static_str() {
+            s.len()
+        } else if self.is_heap_allocated() {
             heap_capacity(self)
         } else {
             MAX_SIZE
@@ -439,11 +461,44 @@ impl Repr {
         last_byte == HEAP_MASK
     }
 
+    #[inline(always)]
+    const fn is_static_str(&self) -> bool {
+        let last_byte = self.last_byte();
+        last_byte == STATIC_STR_MASK
+    }
+
+    #[inline]
+    #[rustversion::attr(since(1.64), const)]
+    pub fn as_static_str(&self) -> Option<&'static str> {
+        if self.is_static_str() {
+            // SAFETY: A `Repr` is transmuted from `StaticStr`
+            let s: &StaticStr = unsafe { &*(self as *const Self as *const StaticStr) };
+            Some(s.get_text())
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    fn as_static_variant_mut(&mut self) -> Option<&mut StaticStr> {
+        if self.is_static_str() {
+            // SAFETY: A `Repr` is transmuted from `StaticStr`
+            let s: &mut StaticStr = unsafe { &mut *(self as *mut Self as *mut StaticStr) };
+            Some(s)
+        } else {
+            None
+        }
+    }
+
     /// Return a mutable reference to the entirely underlying buffer
     ///
     /// # Safety
     /// * Callers must guarantee that any modifications made to the buffer are valid UTF-8
     pub unsafe fn as_mut_buf(&mut self) -> &mut [u8] {
+        if let Some(s) = self.as_static_str() {
+            *self = Repr::new(s);
+        }
+
         // the last byte stores our discriminant and stack length
         let last_byte = self.last_byte();
 
@@ -469,9 +524,9 @@ impl Repr {
     /// * `len` bytes in the buffer must be valid UTF-8
     /// * If the underlying buffer is stored inline, `len` must be <= MAX_SIZE
     pub unsafe fn set_len(&mut self, len: usize) {
-        let last_byte = self.last_byte();
-
-        if last_byte == HEAP_MASK {
+        if let Some(s) = self.as_static_variant_mut() {
+            s.set_len(len);
+        } else if self.is_heap_allocated() {
             // SAFETY: We just checked the discriminant to make sure we're heap allocated
             let heap_buffer = self.as_mut_heap();
             // SAFETY: The caller guarantees that `len` bytes is valid UTF-8
@@ -599,50 +654,25 @@ impl Repr {
         // SAFETY: An `InlineBuffer` and `Repr` have the same size
         &mut *(self as *mut _ as *mut InlineBuffer)
     }
-
-    /// Reinterprets a `&Repr` as an `&InlineBuffer`
-    ///
-    /// # SAFETY
-    /// * The caller must guarantee that the provided [`Repr`] is actually an [`InlineBuffer`] by
-    /// checking the discriminant
-    ///
-    /// Note: We used to define [`Repr`] as a `union` which implicitly transmuted between the two
-    /// types, but that prevented us from defining a "niche" value to make `Option<CompactString>`
-    /// the same size as just `CompactString`
-    #[inline(always)]
-    unsafe fn as_inline(&self) -> &InlineBuffer {
-        // SAFETY: An `InlineBuffer` and `Repr` have the same size
-        &*(self as *const _ as *const InlineBuffer)
-    }
 }
 
 impl Clone for Repr {
     #[inline]
     fn clone(&self) -> Self {
-        let last_byte = self.last_byte();
-
-        #[cold]
+        #[inline(never)]
         fn clone_heap(this: &Repr) -> Repr {
-            // SAFETY: We just checked the discriminant to make sure we're heap allocated
-            let heap = unsafe { this.as_heap() };
-
-            // If the contained string is small enough, we will inline it instead of allocating
-            if heap.len <= MAX_SIZE {
-                // SAFETY: Checked to make sure the length is <= MAX_SIZE
-                let inline = unsafe { InlineBuffer::new(this.as_str()) };
-                Repr::from_inline(inline)
-            } else {
-                let new = heap.clone();
-                Repr::from_heap(new)
-            }
+            Repr::new(this.as_str())
         }
 
-        if last_byte == HEAP_MASK {
+        // There are only two cases we need to care about: If the string is allocated on the heap
+        // or not. If it is, then the data must be cloned proberly, otherwise we can simply copy
+        // the `Repr`.
+        if self.is_heap_allocated() {
             clone_heap(self)
         } else {
-            // SAFETY: We checked above that the discriminant indicates we're inline
-            let inline = unsafe { self.as_inline() };
-            Repr::from_inline(inline.copy())
+            // SAFETY: We just checked that `self` can be copied because it is an inline string or
+            // a reference to a `&'static str`.
+            unsafe { core::ptr::read(self) }
         }
     }
 }
@@ -713,6 +743,12 @@ impl Extend<String> for Repr {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::{
+        String,
+        ToString,
+    };
+    use alloc::vec::Vec;
+
     use quickcheck_macros::quickcheck;
     use test_case::test_case;
 
@@ -722,8 +758,7 @@ mod tests {
     };
 
     const EIGHTEEN_MB: usize = 18 * 1024 * 1024;
-    const EIGHTEEN_MB_STR: &'static str =
-        unsafe { core::str::from_utf8_unchecked(&[42; EIGHTEEN_MB]) };
+    const EIGHTEEN_MB_STR: &str = unsafe { core::str::from_utf8_unchecked(&[42; EIGHTEEN_MB]) };
 
     #[test_case("hello world!"; "inline")]
     #[test_case("this is a long string that should be stored on the heap"; "heap")]
@@ -731,6 +766,11 @@ mod tests {
         let repr = Repr::new(s);
         assert_eq!(repr.as_str(), s);
         assert_eq!(repr.len(), s.len());
+
+        // test StaticStr variant
+        let repr = Repr::from_static_str(s);
+        assert_eq!(repr.as_str(), s);
+        assert_eq!(repr.len(), s.len());
     }
 
     #[quickcheck]
@@ -839,6 +879,13 @@ mod tests {
 
         assert_eq!(control.len(), s.len());
         assert_eq!(control, s.as_str());
+
+        // test StaticStr variant
+        let r = Repr::from_static_str(control);
+        let s = r.into_string();
+
+        assert_eq!(control.len(), s.len());
+        assert_eq!(control, s.as_str());
     }
 
     #[quickcheck]
@@ -866,6 +913,18 @@ mod tests {
         assert_eq!(r.len(), c.len());
 
         assert_eq!(r.is_heap_allocated(), is_heap);
+
+        // test StaticStr variant
+        let mut r = Repr::from_static_str(control);
+        let mut c = String::from(control);
+
+        r.push_str(append);
+        c.push_str(append);
+
+        assert_eq!(r.as_str(), c.as_str());
+        assert_eq!(r.len(), c.len());
+
+        assert_eq!(r.is_heap_allocated(), is_heap);
     }
 
     #[quickcheck]
@@ -918,6 +977,13 @@ mod tests {
 
         assert!(r.capacity() >= initial.len() + additional);
         assert_eq!(r.is_heap_allocated(), is_heap);
+
+        // Test static_str variant
+        let mut r = Repr::from_static_str(initial);
+        r.reserve(additional);
+
+        assert!(r.capacity() >= initial.len() + additional);
+        assert_eq!(r.is_heap_allocated(), is_heap);
     }
 
     #[test]
@@ -942,6 +1008,18 @@ mod tests {
         assert_eq!(r_a.len(), r_b.len());
         assert_eq!(r_a.capacity(), r_b.capacity());
         assert_eq!(r_a.is_heap_allocated(), r_b.is_heap_allocated());
+
+        // test StaticStr variant
+        let r_a = Repr::from_static_str(initial);
+        let r_b = r_a.clone();
+
+        assert_eq!(r_a.as_str(), initial);
+        assert_eq!(r_a.len(), initial.len());
+
+        assert_eq!(r_a.as_str(), r_b.as_str());
+        assert_eq!(r_a.len(), r_b.len());
+        assert_eq!(r_a.capacity(), r_b.capacity());
+        assert_eq!(r_a.is_heap_allocated(), r_b.is_heap_allocated());
     }
 
     #[quickcheck]
diff --git a/compact_str/src/repr/num.rs b/compact_str/src/repr/num.rs
index c2baa13d..af49f769 100644
--- a/compact_str/src/repr/num.rs
+++ b/compact_str/src/repr/num.rs
@@ -418,6 +418,8 @@ impl NumChars for isize {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::ToString;
+
     use super::IntoRepr;
 
     #[test]
diff --git a/compact_str/src/repr/smallvec.rs b/compact_str/src/repr/smallvec.rs
index f1538866..5f42641d 100644
--- a/compact_str/src/repr/smallvec.rs
+++ b/compact_str/src/repr/smallvec.rs
@@ -2,7 +2,6 @@ use smallvec::SmallVec;
 
 use super::{
     Repr,
-    HEAP_MASK,
     MAX_SIZE,
 };
 
@@ -12,9 +11,9 @@ impl Repr {
     /// Note: both for the inlined case and the heap case, the buffers are re-used
     #[inline]
     pub fn into_bytes(self) -> SmallVec<[u8; MAX_SIZE]> {
-        let last_byte = self.last_byte();
-
-        if last_byte == HEAP_MASK {
+        if let Some(s) = self.as_static_str() {
+            SmallVec::from(s.as_bytes())
+        } else if self.is_heap_allocated() {
             let string = self.into_string();
             let bytes = string.into_bytes();
             SmallVec::from_vec(bytes)
@@ -44,5 +43,14 @@ mod tests {
 
         let ex_compact = CompactString::from_utf8(bytes).unwrap();
         assert_eq!(ex_compact, s);
+
+        // test `StaticStr` variant
+        let og_compact = CompactString::from_static_str(s);
+        assert_eq!(og_compact, s);
+
+        let bytes = og_compact.into_bytes();
+
+        let ex_compact = CompactString::from_utf8(bytes).unwrap();
+        assert_eq!(ex_compact, s);
     }
 }
diff --git a/compact_str/src/repr/static_str.rs b/compact_str/src/repr/static_str.rs
new file mode 100644
index 00000000..404cfd9b
--- /dev/null
+++ b/compact_str/src/repr/static_str.rs
@@ -0,0 +1,56 @@
+use core::{
+    mem,
+    ptr,
+    slice,
+    str,
+};
+
+use super::{
+    Repr,
+    MAX_SIZE,
+    STATIC_STR_MASK,
+};
+
+pub(super) const DISCRIMINANT_SIZE: usize = MAX_SIZE - mem::size_of::<&'static str>();
+
+/// A buffer stored on the stack whose size is equal to the stack size of `String`
+/// The last byte is set to 0.
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct StaticStr {
+    ptr: ptr::NonNull<u8>,
+    len: usize,
+    #[allow(unused)]
+    discriminant: [u8; DISCRIMINANT_SIZE],
+}
+static_assertions::assert_eq_size!(StaticStr, Repr);
+static_assertions::assert_eq_size!(&'static str, (*const u8, usize));
+
+impl StaticStr {
+    #[inline]
+    pub const fn new(text: &'static str) -> Self {
+        let mut discriminant = [0; DISCRIMINANT_SIZE];
+        discriminant[DISCRIMINANT_SIZE - 1] = STATIC_STR_MASK;
+
+        Self {
+            // SAFETY: `&'static str` must have a non-null, properly aligned
+            // address
+            ptr: unsafe { ptr::NonNull::new_unchecked(text.as_ptr() as *mut _) },
+            len: text.len(),
+            discriminant,
+        }
+    }
+
+    #[rustversion::attr(since(1.64), const)]
+    pub(super) fn get_text(&self) -> &'static str {
+        // SAFETY: `StaticStr` invariants requires it to be a valid str
+        unsafe { str::from_utf8_unchecked(slice::from_raw_parts(self.ptr.as_ptr(), self.len)) }
+    }
+
+    /// # Safety
+    /// * `len` bytes in the buffer must be valid UTF-8 and
+    /// * `len` must be <= `self.get_text().len()`
+    pub(super) unsafe fn set_len(&mut self, len: usize) {
+        *self = Self::new(&self.get_text()[..len]);
+    }
+}
diff --git a/compact_str/src/repr/traits.rs b/compact_str/src/repr/traits.rs
index b75b91cf..28b7b591 100644
--- a/compact_str/src/repr/traits.rs
+++ b/compact_str/src/repr/traits.rs
@@ -43,6 +43,8 @@ impl IntoRepr for char {
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::ToString;
+
     use quickcheck_macros::quickcheck;
 
     use super::IntoRepr;
diff --git a/compact_str/src/traits.rs b/compact_str/src/traits.rs
index 20e273c7..a1261bbc 100644
--- a/compact_str/src/traits.rs
+++ b/compact_str/src/traits.rs
@@ -4,6 +4,10 @@ use core::fmt::{
 };
 use core::num;
 
+// TODO(parkmycar): Re-enable when a new version of `castaway` is released.
+//
+// <https://github.com/ParkMyCar/compact_str/issues/304>
+// use alloc::string::String;
 use castaway::{
     match_type,
     LifetimeFree,
@@ -85,7 +89,10 @@ impl<T: fmt::Display> ToCompactString for T {
             &f64 as s => s.into_repr(),
             &bool as s => s.into_repr(),
             &char as s => s.into_repr(),
-            &String as s => Repr::new(s),
+            // TODO(parkmycar): Re-enable when a new version of `castaway` is released.
+            //
+            // <https://github.com/ParkMyCar/compact_str/issues/304>
+            // &String as s => Repr::new(s),
             &CompactString as s => Repr::new(s),
             &num::NonZeroU8 as s => s.into_repr(),
             &num::NonZeroI8 as s => s.into_repr(),
@@ -192,6 +199,11 @@ where
 
 #[cfg(test)]
 mod tests {
+    use alloc::string::{
+        String,
+        ToString,
+    };
+    use alloc::vec::Vec;
     use core::num;
 
     use proptest::prelude::*;
diff --git a/compact_str/src/unicode_data.rs b/compact_str/src/unicode_data.rs
index 1393b4fc..7fe99aba 100644
--- a/compact_str/src/unicode_data.rs
+++ b/compact_str/src/unicode_data.rs
@@ -8,7 +8,7 @@ fn skip_search<const SOR: usize, const OFFSETS: usize>(
     offsets: &[u8; OFFSETS],
 ) -> bool {
     // Note that this *cannot* be past the end of the array, as the last
-    // element is greater than std::char::MAX (the largest possible needle).
+    // element is greater than char::MAX (the largest possible needle).
     //
     // So, we cannot have found it (i.e. Ok(idx) + 1 != length) and the correct
     // location cannot be past it, so Err(idx) != length either.


Test patch:
diff --git a/compact_str/src/features/proptest.rs b/compact_str/src/features/proptest.rs
index d756fd61..d1cfd1fe 100644
--- a/compact_str/src/features/proptest.rs
+++ b/compact_str/src/features/proptest.rs
@@ -1,5 +1,7 @@
 //! Implements the [`proptest::arbitrary::Arbitrary`] trait for [`CompactString`]
 
+use alloc::string::String;
+
 use proptest::arbitrary::{
     Arbitrary,
     StrategyFor,
@@ -25,11 +27,13 @@ impl Arbitrary for CompactString {
 
 #[cfg(test)]
 mod test {
+    use alloc::string::String;
+
     use proptest::prelude::*;
 
     use crate::CompactString;
 
-    const MAX_SIZE: usize = std::mem::size_of::<String>();
+    const MAX_SIZE: usize = core::mem::size_of::<String>();
 
     proptest! {
         #[test]
diff --git a/compact_str/src/tests.rs b/compact_str/src/tests.rs
index 757acd40..7a5b45f1 100644
--- a/compact_str/src/tests.rs
+++ b/compact_str/src/tests.rs
@@ -1,11 +1,20 @@
-use core::slice;
-use std::borrow::Cow;
-use std::num;
-use std::str::FromStr;
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
+use alloc::string::{
+    String,
+    ToString,
+};
+use alloc::vec::Vec;
+use core::str::FromStr;
+use core::{
+    num,
+    slice,
+};
 
 use proptest::collection::SizeRange;
 use proptest::prelude::*;
 use proptest::strategy::Strategy;
+use test_case::test_case;
 use test_strategy::proptest;
 
 use crate::{
@@ -307,9 +316,9 @@ fn proptest_from_utf8_unchecked(#[strategy(rand_bytes())] bytes: Vec<u8>) {
 
     // check if we were valid UTF-8, if so, assert the data written into the CompactString is
     // correct
-    let data_is_valid = std::str::from_utf8(&bytes);
-    let compact_is_valid = std::str::from_utf8(compact.as_bytes());
-    let std_str_is_valid = std::str::from_utf8(std_str.as_bytes());
+    let data_is_valid = core::str::from_utf8(&bytes);
+    let compact_is_valid = core::str::from_utf8(compact.as_bytes());
+    let std_str_is_valid = core::str::from_utf8(std_str.as_bytes());
 
     match (data_is_valid, compact_is_valid, std_str_is_valid) {
         (Ok(d), Ok(c), Ok(s)) => {
@@ -375,18 +384,34 @@ fn test_const_creation() {
     const EMPTY: CompactString = CompactString::new_inline("");
     const SHORT: CompactString = CompactString::new_inline("rust");
 
+    const EMPTY_STATIC_STR: CompactString = CompactString::from_static_str("");
+    const SHORT_STATIC_STR: CompactString = CompactString::from_static_str("rust");
+
     #[cfg(target_pointer_width = "64")]
     const PACKED: CompactString = CompactString::new_inline("i am 24 characters long!");
     #[cfg(target_pointer_width = "32")]
     const PACKED: CompactString = CompactString::new_inline("i am 12 char");
 
+    const PACKED_STATIC_STR0: CompactString =
+        CompactString::from_static_str("i am 24 characters long!");
+    const PACKED_STATIC_STR1: CompactString = CompactString::from_static_str("i am 12 char");
+
     assert_eq!(EMPTY, CompactString::new(""));
     assert_eq!(SHORT, CompactString::new("rust"));
 
+    assert_eq!(EMPTY_STATIC_STR, CompactString::new(""));
+    assert_eq!(SHORT_STATIC_STR, CompactString::new("rust"));
+
     #[cfg(target_pointer_width = "64")]
     assert_eq!(PACKED, CompactString::new("i am 24 characters long!"));
     #[cfg(target_pointer_width = "32")]
     assert_eq!(PACKED, CompactString::new("i am 12 char"));
+
+    assert_eq!(
+        PACKED_STATIC_STR0,
+        CompactString::new("i am 24 characters long!")
+    );
+    assert_eq!(PACKED_STATIC_STR1, CompactString::new("i am 12 char"));
 }
 
 #[test]
@@ -478,7 +503,6 @@ fn test_from_str_trait() {
 #[cfg_attr(target_pointer_width = "32", ignore)]
 fn test_from_char_iter() {
     let s = "\u{0} 0 \u{0}a𐀀𐀀 𐀀a𐀀";
-    println!("{}", s.len());
     let compact: CompactString = s.chars().into_iter().collect();
 
     assert!(!compact.is_heap_allocated());
@@ -500,11 +524,10 @@ fn test_extend_packed_from_empty() {
     assert!(!compact.is_heap_allocated());
 }
 
-#[test]
-fn test_pop_empty() {
+#[test_case(CompactString::from(""); "inline")]
+#[test_case(CompactString::from_static_str(""); "static_str")]
+fn test_pop_empty(mut compact: CompactString) {
     let num_pops = 256;
-    let mut compact = CompactString::from("");
-
     (0..num_pops).for_each(|_| {
         let ch = compact.pop();
         assert!(ch.is_none());
@@ -532,12 +555,11 @@ fn test_compact_str_is_send_and_sync() {
     is_send_and_sync::<CompactString>();
 }
 
-#[test]
-fn test_fmt_write() {
+#[test_case(CompactString::default(); "inline")]
+#[test_case(CompactString::from_static_str(""); "static_str")]
+fn test_fmt_write(mut compact: CompactString) {
     use core::fmt::Write;
 
-    let mut compact = CompactString::default();
-
     write!(compact, "test").unwrap();
     assert_eq!(compact, "test");
 
@@ -575,6 +597,42 @@ fn test_plus_operator() {
     assert_eq!(String::from("a") + &"b", "ab");
 }
 
+#[test]
+fn test_plus_operator_static_str() {
+    // + &CompactString
+    assert_eq!(
+        CompactString::from_static_str("a") + &CompactString::from_static_str("b"),
+        "ab"
+    );
+    // + &str
+    assert_eq!(CompactString::from_static_str("a") + "b", "ab");
+    // + &String
+    assert_eq!(
+        CompactString::from_static_str("a") + &String::from("b"),
+        "ab"
+    );
+    // + &Box<str>
+    let box_str = String::from("b").into_boxed_str();
+    assert_eq!(CompactString::from_static_str("a") + &box_str, "ab");
+    // + &Cow<'a, str>
+    let cow = Cow::from("b");
+    assert_eq!(CompactString::from_static_str("a") + &cow, "ab");
+
+    // Implementing `Add<T> for String` can break adding &String or other types to String, so we
+    // explicitly don't do this. See https://github.com/rust-lang/rust/issues/77143 for more details.
+    // Below we assert adding types to String still compiles
+
+    // String + &CompactString
+    assert_eq!(
+        String::from("a") + &CompactString::from_static_str("b"),
+        "ab"
+    );
+    // String + &String
+    assert_eq!(String::from("a") + &("b".to_string()), "ab");
+    // String + &str
+    assert_eq!(String::from("a") + &"b", "ab");
+}
+
 #[test]
 fn test_plus_equals_operator() {
     let mut m = CompactString::from("a");
@@ -582,6 +640,13 @@ fn test_plus_equals_operator() {
     assert_eq!(m, "ab");
 }
 
+#[test]
+fn test_plus_equals_operator_static_str() {
+    let mut m = CompactString::from_static_str("a");
+    m += "b";
+    assert_eq!(m, "ab");
+}
+
 #[test]
 fn test_u8_to_compact_string() {
     let vals = [u8::MIN, 1, 42, u8::MAX - 2, u8::MAX - 1, u8::MAX];
@@ -1083,6 +1148,23 @@ fn test_into_string_small_str() {
     assert_eq!(str_len, new_str_cap);
 }
 
+#[test]
+fn test_into_string_small_static_str() {
+    let data = "abcdef";
+    let str_addr = data.as_ptr();
+    let str_len = data.len();
+
+    let compact = CompactString::from_static_str(data);
+    let new_string = String::from(compact);
+    let new_str_addr = new_string.as_ptr();
+    let new_str_len = new_string.len();
+    let new_str_cap = new_string.capacity();
+
+    assert_ne!(str_addr, new_str_addr);
+    assert_eq!(str_len, new_str_len);
+    assert_eq!(str_len, new_str_cap);
+}
+
 #[test]
 fn test_into_string_long_str() {
     let data = "this is a long string that will be on the heap";
@@ -1100,6 +1182,23 @@ fn test_into_string_long_str() {
     assert_eq!(str_len, new_str_cap);
 }
 
+#[test]
+fn test_into_string_long_static_str() {
+    let data = "this is a long string that will be on the heap";
+    let str_addr = data.as_ptr();
+    let str_len = data.len();
+
+    let compact = CompactString::from_static_str(data);
+    let new_string = String::from(compact);
+    let new_str_addr = new_string.as_ptr();
+    let new_str_len = new_string.len();
+    let new_str_cap = new_string.capacity();
+
+    assert_ne!(str_addr, new_str_addr);
+    assert_eq!(str_len, new_str_len);
+    assert_eq!(str_len, new_str_cap);
+}
+
 #[test]
 fn test_into_string_empty_str() {
     let data = "";
@@ -1116,6 +1215,22 @@ fn test_into_string_empty_str() {
     assert_eq!(str_len, new_str_cap);
 }
 
+#[test]
+fn test_into_string_empty_static_str() {
+    let data = "";
+    let str_len = data.len();
+
+    let compact = CompactString::from_static_str(data);
+    let new_string = String::from(compact);
+    let new_str_addr = new_string.as_ptr();
+    let new_str_len = new_string.len();
+    let new_str_cap = new_string.capacity();
+
+    assert_eq!(String::new().as_ptr(), new_str_addr);
+    assert_eq!(str_len, new_str_len);
+    assert_eq!(str_len, new_str_cap);
+}
+
 #[test]
 fn test_truncate_noops_if_new_len_greater_than_current() {
     let mut short = CompactString::from("short");
@@ -1133,6 +1248,22 @@ fn test_truncate_noops_if_new_len_greater_than_current() {
     assert_eq!(long.capacity(), long_cap);
 }
 
+#[test]
+fn test_truncate_noops_if_new_len_greater_than_current_static_str() {
+    let mut short = CompactString::from_static_str("short");
+    short.truncate(100);
+
+    assert_eq!(short.len(), 5);
+    assert_eq!(short.capacity(), 5);
+
+    let mut long =
+        CompactString::from_static_str("i am a long string that will be allocated on the heap");
+    long.truncate(500);
+
+    assert_eq!(long.len(), 53);
+    assert_eq!(long.capacity(), 53);
+}
+
 #[test]
 #[should_panic(expected = "new_len must lie on char boundary")]
 fn test_truncate_panics_on_non_char_boundary() {
@@ -1141,78 +1272,79 @@ fn test_truncate_panics_on_non_char_boundary() {
     emojis.truncate(1);
 }
 
-#[test]
-fn test_insert() {
+#[test_case(CompactString::from; "inline")]
+#[test_case(CompactString::from_static_str; "static_str")]
+fn test_insert(to_compact: fn(&'static str) -> CompactString) {
     // insert into empty string
-    let mut one_byte = CompactString::from("");
+    let mut one_byte = to_compact("");
     one_byte.insert(0, '.');
     assert_eq!(one_byte, ".");
 
-    let mut two_bytes = CompactString::from("");
+    let mut two_bytes = to_compact("");
     two_bytes.insert(0, 'Ü');
     assert_eq!(two_bytes, "Ü");
 
-    let mut three_bytes = CompactString::from("");
+    let mut three_bytes = to_compact("");
     three_bytes.insert(0, '€');
     assert_eq!(three_bytes, "€");
 
-    let mut four_bytes = CompactString::from("");
+    let mut four_bytes = to_compact("");
     four_bytes.insert(0, '😀');
     assert_eq!(four_bytes, "😀");
 
     // insert at the front of string
-    let mut one_byte = CompactString::from("😀");
+    let mut one_byte = to_compact("😀");
     one_byte.insert(0, '.');
     assert_eq!(one_byte, ".😀");
 
-    let mut two_bytes = CompactString::from("😀");
+    let mut two_bytes = to_compact("😀");
     two_bytes.insert(0, 'Ü');
     assert_eq!(two_bytes, "Ü😀");
 
-    let mut three_bytes = CompactString::from("😀");
+    let mut three_bytes = to_compact("😀");
     three_bytes.insert(0, '€');
     assert_eq!(three_bytes, "€😀");
 
-    let mut four_bytes = CompactString::from("😀");
+    let mut four_bytes = to_compact("😀");
     four_bytes.insert(0, '😀');
     assert_eq!(four_bytes, "😀😀");
 
     // insert at the end of string
-    let mut one_byte = CompactString::from("😀");
+    let mut one_byte = to_compact("😀");
     one_byte.insert(4, '.');
     assert_eq!(one_byte, "😀.");
 
-    let mut two_bytes = CompactString::from("😀");
+    let mut two_bytes = to_compact("😀");
     two_bytes.insert(4, 'Ü');
     assert_eq!(two_bytes, "😀Ü");
 
-    let mut three_bytes = CompactString::from("😀");
+    let mut three_bytes = to_compact("😀");
     three_bytes.insert(4, '€');
     assert_eq!(three_bytes, "😀€");
 
-    let mut four_bytes = CompactString::from("😀");
+    let mut four_bytes = to_compact("😀");
     four_bytes.insert(4, '😀');
     assert_eq!(four_bytes, "😀😀");
 
     // insert in the middle of string
-    let mut one_byte = CompactString::from("😀😀");
+    let mut one_byte = to_compact("😀😀");
     one_byte.insert(4, '.');
     assert_eq!(one_byte, "😀.😀");
 
-    let mut two_bytes = CompactString::from("😀😀");
+    let mut two_bytes = to_compact("😀😀");
     two_bytes.insert(4, 'Ü');
     assert_eq!(two_bytes, "😀Ü😀");
 
-    let mut three_bytes = CompactString::from("😀😀");
+    let mut three_bytes = to_compact("😀😀");
     three_bytes.insert(4, '€');
     assert_eq!(three_bytes, "😀€😀");
 
-    let mut four_bytes = CompactString::from("😀😀");
+    let mut four_bytes = to_compact("😀😀");
     four_bytes.insert(4, '😀');
     assert_eq!(four_bytes, "😀😀😀");
 
     // edge case: new length is 24 bytes
-    let mut s = CompactString::from("\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}");
+    let mut s = to_compact("\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}\u{ffff}");
     s.insert(21, '\u{ffff}');
     assert_eq!(
         s,
@@ -1246,6 +1378,13 @@ fn test_remove_empty_string() {
     compact.remove(0);
 }
 
+#[test]
+#[should_panic(expected = "cannot remove a char from the end of a string")]
+fn test_remove_empty_string_static() {
+    let mut compact = CompactString::from_static_str("");
+    compact.remove(0);
+}
+
 #[test]
 #[should_panic(expected = "cannot remove a char from the end of a string")]
 fn test_remove_str_len() {
@@ -1349,6 +1488,51 @@ fn test_reserve_shrink_roundtrip() {
     assert_eq!(s, TEXT);
 }
 
+#[test]
+fn test_reserve_shrink_roundtrip_static() {
+    const TEXT: &str = "Hello.";
+
+    let mut s = CompactString::from_static_str(TEXT);
+    assert!(!s.is_heap_allocated());
+    assert_eq!(s.capacity(), s.len());
+    assert_eq!(s, TEXT);
+
+    s.reserve(128);
+    assert!(s.is_heap_allocated());
+    assert!(s.capacity() >= 128 + TEXT.len());
+    assert_eq!(s, TEXT);
+
+    s.shrink_to(64);
+    assert!(s.is_heap_allocated());
+    assert!(s.capacity() >= 64);
+    assert_eq!(s, TEXT);
+
+    s.shrink_to_fit();
+    assert!(!s.is_heap_allocated());
+    assert_eq!(s.capacity(), MAX_SIZE);
+    assert_eq!(s, TEXT);
+
+    s.reserve(SIXTEEN_MB);
+    assert!(s.is_heap_allocated());
+    assert!(s.capacity() >= SIXTEEN_MB + TEXT.len());
+    assert_eq!(s, TEXT);
+
+    s.shrink_to(64);
+    assert!(s.is_heap_allocated());
+    assert!(s.capacity() >= 64);
+    assert_eq!(s, TEXT);
+
+    s.reserve(SIXTEEN_MB);
+    assert!(s.is_heap_allocated());
+    assert!(s.capacity() >= SIXTEEN_MB + TEXT.len());
+    assert_eq!(s, TEXT);
+
+    s.shrink_to_fit();
+    assert!(!s.is_heap_allocated());
+    assert_eq!(s.capacity(), MAX_SIZE);
+    assert_eq!(s, TEXT);
+}
+
 #[test]
 fn test_from_utf8_unchecked_sanity() {
     let text = "hello 🌎, you are nice";
@@ -1556,7 +1740,7 @@ fn test_collect() {
 fn test_into_cow() {
     let og = "aaa";
     let compact = CompactString::new(og);
-    let cow: std::borrow::Cow<'_, str> = compact.into();
+    let cow: alloc::borrow::Cow<'_, str> = compact.into();
 
     assert_eq!(og, cow);
 }
@@ -1581,6 +1765,9 @@ fn test_from_string_buffer_inlines_on_clone() {
     assert!(!b.is_heap_allocated());
 }
 
+// This feature was enabled by <https://github.com/rust-lang/rust/pull/94075> which was first
+// released in Rust 1.65.
+#[rustversion::since(1.65)]
 #[test]
 fn multiple_nieches_test() {
     #[allow(unused)]
@@ -1591,5 +1778,8 @@ fn multiple_nieches_test() {
         Unsigned(usize),
         Null,
     }
-    assert_eq!(std::mem::size_of::<Value>(), std::mem::size_of::<String>());
+    assert_eq!(
+        core::mem::size_of::<Value>(),
+        core::mem::size_of::<String>()
+    );
 }