There are lots of reasons it can be. Here's an example: in many languages, concatenation requires reallocation to generate a new string object. This is especially the case where strings are immutable. That can get expensive quickly as you're producing lots of garbage for little benefit, and each concatenation requires progressively larger and larger reallocations. You end up seeing quadratic runtime costs in terms of both memory and speed. There are a few different approaches taken to this. In Python, the popular option is to append everything to a list, and use `''.join(lst)` at the end to concatenate everything. Java and C#, on the other hand, have StringBuffer/StringBuilder types to accomplish something similar.
Generally, you want to avoid simple string concatenation in loops because of this, and it's not always obvious when string concatenation is being done in a loop, such as where the string concatenation is being done in a method that's being called by some other code.
String concatenation isn't expensive in Rust. Since the + operator consumes the first argument it will reuse the buffer and allocate quadratically. The only possible performance downside is that it can't count all of the lengths in advance and do a single allocation.
Add<&str> for String in a loop isn't as problematic as adding two, for example, Java Strings because of the capacity growth curve and buffer reuse, but it is still worse than not having a `String::with_capacity` call before the loop. I am sad to see that clippy doesn't have any lints for this, but I can see how it would be hard to make a general good suggestion for it.
Keep in mind that the potential expense of string concatenation was only _one_ example.
There's a reason why I explicitly mentioned immutability: Rust's `String` type has more in common with C#'s `StringBuilder` class than it does with strings in most languages. Meanwhile, the behavour of str/&str is very different.