Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a char to &str

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.

like image 712
8176135 Avatar asked Dec 04 '17 08:12

8176135


3 Answers

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);
    }
}
like image 134
Shepmaster Avatar answered Sep 18 '22 11:09

Shepmaster


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

like image 30
red75prime Avatar answered Sep 18 '22 11:09

red75prime


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 Strings:

match k {
    SP_KEY_1 => String::from("KEY_1"),
    ...
}

than contorting. Keeping to &strs seems like premature optimization from where I stand.

like image 27
papagaga Avatar answered Sep 22 '22 11:09

papagaga