I wish to convert a String
created using the format!
macro to a &str
and assign this to a value using a let
binding:
fn main() {
let my_bool = true;
let other = String::from("my_string");
let result = if my_bool {
format!("_{}", other).as_str()
} else {
"other"
};
println!("{}", result);
}
(Rust Playground)
When I do this, the compiler complains that the temporary String
value is freed at the end of the statement (from what I've understood), meaning that I'm unable to dynamically create a &str
:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:5:9
|
4 | let result = if my_bool {
| ------ borrow later stored here
5 | format!("_{}", other).as_str()
| ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
6 | } else {
| - temporary value is freed at the end of this statement
|
I've been trying to understand Rust's lifetime system, but I can't really wrap my head around this one. Rust suggests the following:
= note: consider using a `let` binding to create a longer lived value
I wrapped format!("_{}", other)
in a let
binding:
fn main() {
let my_bool = true;
let other = String::from("my_string");
let result = if my_bool {
let s = format!("_{}", other);
s.as_str()
} else {
"other"
};
println!("{}", result);
}
But it doesn't seem to solve the issue, as when I call as_str()
on this binding it still complains that the borrowed value does not live long enough:
error[E0597]: `s` does not live long enough
--> src/main.rs:6:9
|
4 | let result = if my_bool {
| ------ borrow later stored here
5 | let s = format!("_{}", other);
6 | s.as_str()
| ^ borrowed value does not live long enough
7 | } else {
| - `s` dropped here while still borrowed
This works when I omit the whole if
, but I would rather not do this as this would cause a lot of headaches in the original code base.
Also, that does seem like kind of a cop-out, because then I still wouldn't know why this fails.
How would I go about solving this in a systematic manner?
&str
is a borrowed string, so you cannot get one from a temporary String
, otherwise a reference would outlive the value it is bound to.
There are 2 solutions to palliate this problem:
You can create a new variable to store the String
out of the if
scope:
fn main() {
let my_bool = true;
let other = String::from("my_string");
let result;
let result = if my_bool {
result = format!("_{}", other);
result.as_str()
} else {
"other"
};
println!("{}", result);
}
You can use the Cow
type to do what you want:
use std::borrow::Cow;
fn main() {
let my_bool = true;
let other = String::from("my_string");
let result = if my_bool {
Cow::Owned(format!("_{}", other))
} else {
Cow::Borrowed("other")
};
assert_eq!("_my_string", result);
}
Cow
(for clone on write) is an enum
that have either an owned or a borrowed data. In this particular case, result
has the type Cow<str>
.
You can simplify the notation by writing:
let result = if my_bool {
format!("_{}", other).into()
} else {
Cow::Borrowed("other")
};
or (matter of style):
let result: Cow<str> = if my_bool {
format!("_{}", other).into()
} else {
"other".into()
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With