Change &str variable if condition is true

I had this problem multiple times now and wanted to ask this. Is there any smart possibility to do this without allocating a new string if condition() is false?

fn compile_error(mut s: &str) {
    if condition() {
        s = &format!("{}, condition was true", s)
    }
    // Do something with the string, this is just an example
    println!("{}", s);
}

fn works_with_cloning(s: &str) {
    let s = if condition() {
        format!("{}, condition was true", s)
    } else {
        s.to_string() // I want to avoid this
    };
    // Do something with the string, this is just an example
    println!("{}", s);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=93725b2f5e96f477c03168bcdab886a9

This is what the cow type is for.

A bit advanced lifetime usage example here.

fn works(s: &str) {
    let buf;
    let s = if condition() {
        buf = format!("{}, condition was true", s);
        &buf
    } else {
        s
    };
    // Do something with the string, this is just an example
    println!("{}", s);
}
4 Likes

Keep in mind that an &str is a reference to a string stored elsewhere (e.g. a local variable in the calling function). If you could swap out buf with another &str, who would own it once your function returns?

If you just want to swap it out within your function it's perfectly fine to use the Cow<str> trick mentioned by @alice, but if you want the caller to gain access to the string when the condition is true you'll need to come up with a different solution (e.g. returning the new string or passing in an &mut String).

You could just do:

fn works_without_cloning(s: &str) {
    let s = if condition() {
        format!("{}, condition was true", s).as_str() // Fixes your issue.
    } else {
        s // No Cloning
    };
    println!("{}", s);
}

I still get this with your suggestion:

error[E0716]: temporary value dropped while borrowed
 --> src/lib.rs:4:9
  |
3 |     let s = if condition() {
  |         - borrow later stored here
4 |         format!("{}, condition was true", s).as_str() // Fixes your issue.
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
5 |     } else {
  |     - temporary value is freed at the end of this statement
  |
  = note: consider using a `let` binding to create a longer lived value
  = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

The suggestion, consider using a `let` binding to create a longer lived value, is hinting at the solution @Hyeonu suggested.

Yeah, @Hyeonu seems to have the best answer. Sorry for the mistake. I didn't check to see if it would wholly compile.