keddad
keddad

Reputation: 1796

How do I fix "creates a temporary which is freed while still in use" without creating additional variable?

I've encountered a simple task: I want to pass a sum of strings to a constructor of an object. In my case, I wanted to create a Path from a sum of &str, like this:

const BASE_PATH: &str = "/some/path/";

fn main() {
    let write_br_path = Path::new(BASE_PATH + "brightness");
}

However, I soon found out that I can't sum &str and &str, so, as my compiler suggested, I did something like this:

let write_br_path = Path::new(&(BASE_PATH.to_owned() + "brightness"));

However, this also didn't work. This code failed to compile, informing me that "creates a temporary which is freed while still in use". I've come up with a simple solution:

let m_b = BASE_PATH.to_owned() + "max_brightness";
let max_br_path = Path::new(&(m_b));

This leaves me with a question: can this code be written in a more compact way, without additional variable being created?

Upvotes: 2

Views: 3488

Answers (2)

Todd
Todd

Reputation: 5395

To answer this question as it relates to Path::new() specifically, you invoke Path::new() where you need it without assigning it to a variable.

For instance, to pass a Path reference to another function, or macro, such as println!(), invoke Path::new() in the parameter list:

println!("{:?}", Path::new(&[BASE_PATH, "brightness"].concat()));

^^ this works because neither Path::new(), nor .concat()'s return value is being bound to a variable. They're both used once in place.

Although the same code worked when passed to println!() in the example above, the same code in the following example doesn't work because the return value of Path::new() has a let binding, but the actual data it depends on doesn't:

let p = Path::new(&[BASE_PATH, "brightness"].concat()); // Error...

The reason for the need to assign to a new variable is peculiar to Path::new() since it's a 0-cost conversion operation. It doesn't otherwise do anything with the value passed to it, and it returns a reference rather than a concrete object.

From doc on Path::new():

pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path

Directly wraps a string slice as a Path slice. This is a cost-free conversion.

If you're going to bind the return value of Path::new() to a variable, the actual data it depends on needs to be a static string or a string also bound to a variable.

There are a lot of ways to combine strings. This page gives a number of options and has benchmark figures on the performance of each.

Upvotes: 2

kmdreko
kmdreko

Reputation: 60051

How do I fix “creates a temporary which is freed while still in use” without creating additional variable?

You can refactor your code into a match where the variable is bound via an arm:

match Path::new(&(BASE_PATH.to_owned() + "brightness")) {
    write_br_path => {
        println!("{:?}", write_br_path);
    }
};

Since it is part of the match expression, the temporary created by BASE_PATH.to_owned() + "brightness" is not dropped until the end of it, meaning you can continue to use write_br_path within the arm without a problem.

I do not actually suggest doing this.

You need an additional variable because of the rules for temporary scopes.

This is probably not as compact as you'd like, but a fairly common thing to do is to make an additional variable and shadow it:

let max_br_path = BASE_PATH.to_owned() + "max_brightness";
let max_br_path = Path::new(&max_br_path);

This can be nice to avoid misusing the original variable and enforce use of its shadow. This is mentioned in the rust book in the guessing game tutorial: "This feature is often used in situations in which you want to convert a value from one type to another type."

Upvotes: 1

Related Questions