diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index f7dff4c21f7c4..dba445050727a 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1982,6 +1982,67 @@ impl Add<&str> for String { } } +/// Implements the `+` operator for concatenating a string and a char together. +/// +/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if +/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on +/// every operation, which would lead to `O(n^2)` running time when building an `n`-byte string by +/// repeated concatenation. +/// +/// # Examples +/// +/// Concatenating a `String` with a `char` takes the `String` by value and copies the `char`: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a + b; +/// // `a` is moved and can no longer be used here. +/// ``` +/// +/// If you want to keep using the initial `String`, you can clone it and append to the +/// clone instead: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a.clone() + b; +/// // `a` is still valid here. +/// ``` +/// +/// Concatenating `char` to a `&str` slice can be done by converting the `&str` to a `String`: +/// +/// ``` +/// let a = "hello world! "; +/// let b = '👋'; +/// let c = a.to_string() + b; +/// ``` +#[stable(feature = "add_string_and_char", since = "1.41.0")] +impl Add for String { + type Output = String; + + #[inline] + fn add(mut self, other: char) -> String { + self.push(other); + self + } +} + +// This had to be added to avoid breakage after adding `impl Add for String` +macro_rules! string_add_impl_extras { + ($($t:ty)*) => ($( + #[stable(feature = "string_add_extras", since = "1.41.0")] + impl Add<$t> for String { + type Output = String; + + #[inline] + fn add(mut self, other: $t) -> String { self.push_str(other); self } + } + )*) +} + +string_add_impl_extras! { &&str &&&str &&&&str &String &&String &&&String &&&&String } + /// Implements the `+=` operator for appending to a `String`. /// /// This has the same behavior as the [`push_str`][String::push_str] method. @@ -1993,6 +2054,31 @@ impl AddAssign<&str> for String { } } +/// Implements the `+=` operator for appending a `char` to a `String`. +/// +/// This has the same behavior as the [`push`][String::push] method. +#[stable(feature = "string_add_assign_char", since = "1.41.0")] +impl AddAssign for String { + #[inline] + fn add_assign(&mut self, other: char) { + self.push(other); + } +} + +// This had to be added to avoid breakage after adding `impl AddAssign for String` +macro_rules! string_addassign_impl_extras { + ($($t:ty)*) => ($( + #[stable(feature = "string_addassign_extras", since = "1.41.0")] + impl AddAssign<$t> for String { + + #[inline] + fn add_assign(&mut self, other: $t) { self.push_str(other); } + } + )*) +} + +string_addassign_impl_extras! { &String &&String &&&String &&&&String &&str &&&str &&&&str } + #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index> for String { type Output = str; diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 55edf56345b59..81f66d9e67f49 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -195,8 +195,10 @@ fn test_add_assign() { assert_eq!(s.as_str(), ""); s += "abc"; assert_eq!(s.as_str(), "abc"); - s += "ประเทศไทย中华Việt Nam"; - assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam"); + s += "ประเทศไทย中华Việt Nam "; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam "); + s += '👋'; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam 👋") } #[test] @@ -304,9 +306,10 @@ fn test_str_clear() { fn test_str_add() { let a = String::from("12345"); let b = a + "2"; - let b = b + "2"; - assert_eq!(b.len(), 7); - assert_eq!(b, "1234522"); + let b = b + "2 "; + let b = b + '👋'; + assert_eq!(b.len(), 12); + assert_eq!(b, "1234522 👋"); } #[test] diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 43c740f7f93f1..321405eb8aba6 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1125,7 +1125,7 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &symbol.as_str(); + let s = String::from("0.") + &*symbol.as_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); } diff --git a/src/test/ui/span/issue-39018.rs b/src/test/ui/span/issue-39018.rs index a3b1d1d81799f..55f8227178930 100644 --- a/src/test/ui/span/issue-39018.rs +++ b/src/test/ui/span/issue-39018.rs @@ -26,7 +26,7 @@ fn foo() { let _ = &a + &b; //~ ERROR binary operation let _ = &a + b; //~ ERROR binary operation let _ = a + &b; // ok - let _ = a + b; //~ ERROR mismatched types + let _ = a + b; //~ ERROR E0277 let _ = e + b; //~ ERROR binary operation let _ = e + &b; //~ ERROR binary operation let _ = e + d; //~ ERROR binary operation diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr index 9637d1d82ec7e..bd7a6c0d0e488 100644 --- a/src/test/ui/span/issue-39018.stderr +++ b/src/test/ui/span/issue-39018.stderr @@ -64,14 +64,13 @@ help: `to_owned()` can be used to create an owned `String` from a string referen LL | let _ = a + &b; | ^ ^^ -error[E0308]: mismatched types - --> $DIR/issue-39018.rs:29:17 +error[E0277]: cannot add `std::string::String` to `std::string::String` + --> $DIR/issue-39018.rs:29:15 | LL | let _ = a + b; - | ^ - | | - | expected `&str`, found struct `std::string::String` - | help: consider borrowing here: `&b` + | ^ no implementation for `std::string::String + std::string::String` + | + = help: the trait `std::ops::Add` is not implemented for `std::string::String` error[E0369]: binary operation `+` cannot be applied to type `&std::string::String` --> $DIR/issue-39018.rs:30:15 @@ -179,5 +178,5 @@ LL | let _ = c.to_owned() + d; error: aborting due to 14 previous errors -Some errors have detailed explanations: E0308, E0369. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`.