To be fair, when doing the naïve thing, the compiler will tell you what you need to do and (tersely) explain why it is needed:
error[E0369]: cannot add `&str` to `&str`
--> src/main.rs:4:15
|
4 | let z = x + y;
| - ^ - &str
| | |
| | `+` cannot be used to concatenate two `&str` strings
| &str
|
help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
|
4 | let z = x.to_owned() + y;
| ^^^^^^^^^^^^
This used not to be the case and tripped people up. some even after being told why it is like this disagree that anyone should care.
Rust is aiming at being performant. The thesis is that having a clarity on the actual behavior of code when reading it helps on this front. Because of this, you need to write more code than you would in other languages, while the upside is that when reading Rust code you can be fairly certain of what the code is doing in the final binary, without any thresholds at play changing the generated code from, for example, monomorphization to dynamic dispatch because you added a method or yet another instance. It's a trade-off. A language that didn't target the embedded and systems space could take all of Rust and radically simplify it, making it closer in experience and performance to Swift. That doesn't mean that the Rust project won't attempt to make things easier to use and write. A possible future move could be to make `Cow<str>` be the default string people actually use, with `&str` continuing to be the preferred argument and relegating `String`s to specialized cases, probably mostly data buffers.
Rust is aiming at being performant. The thesis is that having a clarity on the actual behavior of code when reading it helps on this front. Because of this, you need to write more code than you would in other languages, while the upside is that when reading Rust code you can be fairly certain of what the code is doing in the final binary, without any thresholds at play changing the generated code from, for example, monomorphization to dynamic dispatch because you added a method or yet another instance. It's a trade-off. A language that didn't target the embedded and systems space could take all of Rust and radically simplify it, making it closer in experience and performance to Swift. That doesn't mean that the Rust project won't attempt to make things easier to use and write. A possible future move could be to make `Cow<str>` be the default string people actually use, with `&str` continuing to be the preferred argument and relegating `String`s to specialized cases, probably mostly data buffers.