Push vs + to add to a String

let addr2: String = String::from("14 E 9th Street");

addr2.push_str(" South");
let addr2 = addr2 + " South";

Which of the two lines above is better? Does it make a difference?

Thanks,
Ken

Both works fine, so it's mainly a matter of taste. In theory, the one using + will allocate a new String for the "sum" before dropping the old, while push_str will extend the existing allocation. But in practice, I think the compiler will probably optimize away the difference in most cases. Depending on which parts are hard-coded and which comes from a variable, I often use format!, as in:

let addr2 = format!("{addr2} South"); // maybe?
let south = " South";
let addr2 = format!("14 E 9th Street{south}"); // or like this?

Using format! may not really make sense in this example, but taking a step back and considering where the parts come from, I often find that it can make it all clearer.

1 Like

Hi! The first one is better logically, but practically it doesn't really make sense in terms of memory usage. Anyway, there may be performance overhead, so imho the first one is the best choice.

No, both will work the same way, reusing addr2's buffer if enough space is available.

1 Like

There's no difference. + calls push_str under the hood.

Rust avoids the performance footgun many other languages have where + allocates a new string. It can append directly to the input string because fn add(self, other: &str) -> String consumes the left operand.

Documentation:

impl Add<&str> for String

Implements the + operator for concatenating two strings.

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.

The string on the right-hand side is only borrowed; its contents are copied into the returned String.

Source:

impl Add<&str> for String {
    type Output = String;

    #[inline]
    fn add(mut self, other: &str) -> String {
        self.push_str(other);
        self
    }
}
2 Likes

Aahh! Thank you. That's a relief. Excellent clarity in explaining.

Yes. I had also tried the latter. Worked fine. But I had no clue which is most efficient. I am leaning towards the push as it is obvious (as to what is intended) and (I am guessing) more effecient.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.