I have a match
statement which returns a &str
:
match k {
SP_KEY_1 => "KEY_1",
SP_KEY_2 => "KEY_2",
SP_KEY_3 => "KEY_3",
SP_KEY_4 => "KEY_4",
SP_KEY_5 => "KEY_5",
SP_KEY_6 => "KEY_6",
_ => (k as char), // I want to convert this to &str
}.as_bytes()
I have tried to convert a char
to a string first, and then taking a slice of that:
&(k as char).to_string()[..]
But that gave me the lifetime error:
error[E0597]: borrowed value does not live long enough
Stating that (k as char).to_string()
is a temporary value, which makes sense as from what I can tell to_string()
returns a clone.
I can add .to_string()
to every &str
literal above to make the return value String
, but that seems both ugly (a lot of repeated code), and probably inefficient, as to_string()
clones the original string slice.
The specific question is how would I make a char
into a &str
, but the broader question is that is there a better solution, what is commonly done this situation.
So long as you don't need to return the &str
from the function, you can completely avoid heap allocation using char::encode_utf8
:
const SP_KEY_1: u8 = 0;
const SP_KEY_2: u8 = 1;
const SP_KEY_3: u8 = 2;
const SP_KEY_4: u8 = 3;
const SP_KEY_5: u8 = 4;
const SP_KEY_6: u8 = 5;
fn main() {
let k = 42u8;
let mut tmp = [0; 4];
let s = match k {
SP_KEY_1 => "KEY_1",
SP_KEY_2 => "KEY_2",
SP_KEY_3 => "KEY_3",
SP_KEY_4 => "KEY_4",
SP_KEY_5 => "KEY_5",
SP_KEY_6 => "KEY_6",
_ => (k as char).encode_utf8(&mut tmp),
};
println!("{}", s);
}
This could be paired with a closure if you needed more control:
fn adapt<F, B>(k: u8, f: F) -> B
where
for<'a> F: FnOnce(&'a str) -> B,
{
let mut tmp = [0; 4];
let s = match k {
SP_KEY_1 => "KEY_1",
SP_KEY_2 => "KEY_2",
SP_KEY_3 => "KEY_3",
SP_KEY_4 => "KEY_4",
SP_KEY_5 => "KEY_5",
SP_KEY_6 => "KEY_6",
_ => (k as char).encode_utf8(&mut tmp),
};
f(s)
}
fn main() {
adapt(0, |s| println!("{}", s));
let owned = adapt(0, |s| s.to_owned());
}
Or stored in a struct to provide a little bit of abstraction:
#[derive(Debug, Default)]
struct Foo {
tmp: [u8; 4],
}
impl Foo {
fn adapt(&mut self, k: u8) -> &str {
match k {
SP_KEY_1 => "KEY_1",
SP_KEY_2 => "KEY_2",
SP_KEY_3 => "KEY_3",
SP_KEY_4 => "KEY_4",
SP_KEY_5 => "KEY_5",
SP_KEY_6 => "KEY_6",
_ => (k as char).encode_utf8(&mut self.tmp),
}
}
}
fn main() {
let mut foo = Foo::default();
{
let s = foo.adapt(0);
}
{
let s = foo.adapt(42);
}
}
Using Cow
is simple enough:
use std::borrow::Cow;
fn cow_name(v: u8) -> Cow<'static, str> {
match v {
0 => "KEY_0",
1 => "KEY_1",
_ => return (v as char).to_string().into(),
}.into()
}
Given that k
is a u8
(otherwise your code will not compile), you can also use a constant array:
const NAMES: [&'static str; 256] = [
"KEY_0", "KEY_1", // ...
" ", "!", "\"", "#", // ...
];
fn const_name(k: u8) -> &'static str {
NAMES[k as usize]
}
Playground
Those are two very different things you want to treat in the same way: arrays of bytes known at compile time and a dynamically created array. They aren't allocated in the same area of memory and don't have the same lifetime. You can get away with a reference to a compile-time created array, because they live as long as the program, but you can't with a dynamically created one, since the reference won't last longer than the resource it borrows.
You may be better using only String
s:
match k {
SP_KEY_1 => String::from("KEY_1"),
...
}
than contorting. Keeping to &strs
seems like premature optimization from where I stand.
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