Repository: ParkMyCar/compact_str
Pull request #299 (2023-09-19 20:31:19)

Simplify `clone()`

mcronce: Seems to be a consistent 4% improvement in the `clone large` benchmark with no regression in `clone small`, nice :+1: 

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/compact_str/Cargo.toml b/compact_str/Cargo.toml
index b50f33f6..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,25 +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"
 
-rustversion = "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 b2c354f9..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;
@@ -427,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("�"),
@@ -554,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>] {
@@ -991,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);
@@ -1330,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
@@ -1357,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(()))?);
                 }
             }
@@ -1365,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(()))?);
                 }
             }
@@ -1393,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("�"),
@@ -1404,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("�"),
@@ -1942,6 +1955,7 @@ impl AsRef<str> for CompactString {
     }
 }
 
+#[cfg(feature = "std")]
 impl AsRef<OsStr> for CompactString {
     #[inline]
     fn as_ref(&self) -> &OsStr {
@@ -2294,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 d6df853a..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.
 ///
@@ -240,4 +242,4 @@ pub enum LastUtf8Char {
 }
 
 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 a046e158..1bf37250 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;
@@ -19,6 +20,8 @@ mod num;
 mod static_str;
 mod traits;
 
+use alloc::string::String;
+
 use capacity::Capacity;
 use heap::HeapBuffer;
 use inline::InlineBuffer;
@@ -27,7 +30,7 @@ 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
@@ -81,7 +84,7 @@ 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");
         }
     }
 
@@ -398,9 +401,19 @@ 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),
@@ -644,51 +657,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 let Some(s) = self.as_static_str() {
-            Self::from_static_str(s)
-        } else 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 { std::ptr::read(self) }
         }
     }
 }
@@ -759,6 +746,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;
 
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/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 ee4fa590..7a5b45f1 100644
--- a/compact_str/src/tests.rs
+++ b/compact_str/src/tests.rs
@@ -1,7 +1,15 @@
-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::*;
@@ -308,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)) => {
@@ -495,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());
@@ -1733,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);
 }
@@ -1758,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)]
@@ -1768,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>()
+    );
 }