Repository: ParkMyCar/compact_str
Pull request #130 (2022-06-27 02:39:39)

fix/feat: Change the existing Add<...> impls
Implementing `Add<CompactString> for String` breaks `String + &String`, as long as `compact_str` is used somewhere in your current crate. Specifically this code won't compile:

```
use compact_str;

fn main() {
  println!("{}", "Hello".to_string() + &(" World".to_string()));
}
```

This isn't good! This has to do with Rust's current trait resolver and is a known issue https://github.com/rust-lang/rust/issues/77143

To fix this, we remove `impl Add<CompactString> for String`. You can still add `CompactString` to `String`, you just need to take a reference to it, then the `impl Add<&str> for String` kicks in.

We also remove a lot of `impl Add<...> for CompactString`, leaving just `impl Add<&str> for CompactString`, which allows us to add _more_ string types than we currently can, e.g. `&Box<str>` and `&Cow<str>`. This is because the compiler auto derefs these types to `&str` and uses that impl


Patch:
diff --git a/compact_str/src/lib.rs b/compact_str/src/lib.rs
index 5fb8f89f..76caf34f 100644
--- a/compact_str/src/lib.rs
+++ b/compact_str/src/lib.rs
@@ -879,22 +879,6 @@ impl fmt::Write for CompactString {
     }
 }
 
-impl Add<Self> for CompactString {
-    type Output = Self;
-    fn add(mut self, rhs: Self) -> Self::Output {
-        self.push_str(&rhs);
-        self
-    }
-}
-
-impl Add<&Self> for CompactString {
-    type Output = Self;
-    fn add(mut self, rhs: &Self) -> Self::Output {
-        self.push_str(rhs);
-        self
-    }
-}
-
 impl Add<&str> for CompactString {
     type Output = Self;
     fn add(mut self, rhs: &str) -> Self::Output {
@@ -903,28 +887,4 @@ impl Add<&str> for CompactString {
     }
 }
 
-impl Add<&String> for CompactString {
-    type Output = Self;
-    fn add(mut self, rhs: &String) -> Self::Output {
-        self.push_str(rhs);
-        self
-    }
-}
-
-impl Add<String> for CompactString {
-    type Output = Self;
-    fn add(mut self, rhs: String) -> Self::Output {
-        self.push_str(&rhs);
-        self
-    }
-}
-
-impl Add<CompactString> for String {
-    type Output = Self;
-    fn add(mut self, rhs: CompactString) -> Self::Output {
-        self.push_str(&rhs);
-        self
-    }
-}
-
 crate::asserts::assert_size_eq!(CompactString, String);


Test patch:
diff --git a/compact_str/src/tests.rs b/compact_str/src/tests.rs
index 084c185b..7c354238 100644
--- a/compact_str/src/tests.rs
+++ b/compact_str/src/tests.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
 use std::num;
 use std::str::FromStr;
 
@@ -366,12 +367,29 @@ fn test_fmt_write() {
 
 #[test]
 fn test_plus_operator() {
-    assert_eq!(CompactString::from("a") + CompactString::from("b"), "ab");
+    // + &CompactString
     assert_eq!(CompactString::from("a") + &CompactString::from("b"), "ab");
+    // + &str
     assert_eq!(CompactString::from("a") + "b", "ab");
+    // + &String
     assert_eq!(CompactString::from("a") + &String::from("b"), "ab");
-    assert_eq!(CompactString::from("a") + String::from("b"), "ab");
-    assert_eq!(String::from("a") + CompactString::from("b"), "ab");
+    // + &Box<str>
+    let box_str = String::from("b").into_boxed_str();
+    assert_eq!(CompactString::from("a") + &box_str, "ab");
+    // + &Cow<'a, str>
+    let cow = Cow::from("b");
+    assert_eq!(CompactString::from("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("b"), "ab");
+    // String + &String
+    assert_eq!(String::from("a") + &("b".to_string()), "ab");
+    // String + &str
+    assert_eq!(String::from("a") + &"b", "ab");
 }
 
 #[test]