I happened upon this problem where format!
creates a temporary value in a pattern that is not anchored to anything, as far as I understand it.
let x = 42;
let category = match x {
0...9 => "Between 0 and 9",
number @ 10 => format!("It's a {}!", number).as_str(),
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
In this code, the type of category
is a &str
, which is satisfied by returning a literal like "Between 0 and 9"
. If I want to format the matched value to a slice using as_str()
, then I get an error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:5:24
|
3 | let category = match x {
| -------- borrow later stored here
4 | 0...9 => "Between 0 and 9",
5 | number @ 10 => format!("It's a {}!", number).as_str(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
|
= 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)
I have done some reading, and found people with similar problems, but I couldn't seem to find any solution.
A simple workaround would be to have category
be a String
instead of a &str
, but I don't like the idea of having to put .to_string()
on the end of every literal in the pattern, as it's not as clean.
Is there a way to solve the problem, or do I just need to work around it?
%s specifically is used to perform concatenation of strings together. It allows us to format a value inside a string.
The %s operator lets you add a value into a Python string. The %s signifies that you want to add a string value into a string. The % operator can be used with other configurations, such as %d, to format different types of values.
In java, String format() method returns a formatted string using the given locale, specified format string, and arguments. We can concatenate the strings using this method and at the same time, we can format the output concatenated string.
Return type of format() The format() function returns a formatted representation of the given value as per the format specifier.
This is 90% a duplicate of Return local String as a slice (&str), see that for multiple other solutions.
There's one extra possibility since this is all in one function: You can declare a variable for the String
and only set it when you need to allocate. The compiler (obliquely) suggests this:
consider using a
let
binding to create a longer lived value
fn main() {
let x = 42;
let tmp;
let category = match x {
0...9 => "Between 0 and 9",
number @ 10 => {
tmp = format!("It's a {}!", number);
&tmp
}
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
}
This is mostly the same as using a Cow
, just handled by the compiler instead of a specific type.
See also:
format!
can't return &str
because it will always allocate String
. What is possible to do is to return a &str
from a String
, which is what you did in your code.
As the compiler hinted, the created String
is immediately dropped after its creation because it went out of the current scope and one way around could be an external variable that is not bounded to the match
scope. E.g.:
use std::fmt::Write;
fn main() {
let mut buffer = String::with_capacity(20);
buffer.push_str("It's a ");
let x = 10;
let category = match x {
0...9 => "Between 0 and 9",
number @ 10 => {
write!(&mut buffer, "{}", number).unwrap();
buffer.as_str()
}
_ if x < 0 => "Negative",
_ => "Something else",
};
println!("{}", category);
}
If you want an [no_std]
environment or don't want to do any dynamic allocation, you can take a look at this limited code snippet:
use core::str;
fn each_digit<F>(mut number: u32, mut f: F)
where
F: FnMut(u8),
{
while number > 0 {
f((number % 10) as u8);
number /= 10;
}
}
fn main() {
const BUFFER_LEN: usize = 20;
let mut buffer = [0u8; BUFFER_LEN];
let x = 12344329;
let category = match x {
0...9 => "Between 0 and 9",
number @ 123443219 => {
let mut idx = BUFFER_LEN;
each_digit(number, |digit| {
let ascii = digit + 48;
idx -= 1;
buffer[idx] = ascii;
});
str::from_utf8(&buffer[idx..BUFFER_LEN]).unwrap()
},
_ => "Something else",
};
assert_eq!("123443219", category);
}
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