将一个char转换为&str

11
我有一个返回 &strmatch 语句:
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()

我试过先将一个char转换为字符串,然后对其进行切片:
&(k as char).to_string()[..]

但是这给我带来了终身错误:

error[E0597]: borrowed value does not live long enough

(k as char).to_string() 是一个临时值,这是有道理的,因为根据我的理解,to_string() 返回了一份克隆。

我可以在每个上面的 &str 字面量后添加 .to_string() 以使返回值为 String,但那看起来很丑陋(有很多重复的代码),而且可能效率低下,因为 to_string() 克隆了原始字符串片段。

具体问题是如何将 char 转换为 &str,但更广泛的问题是是否有更好的解决方案,在这种情况下通常如何处理。


2
你需要展示更多的代码,变量 k 是什么类型?它从哪里来的? - ljedrz
3
这显然是一个 XY 问题。除非返回拥有字符的东西,否则无法使本地 char 值的生命周期超过函数作用域。您应该考虑返回其他东西,比如一个枚举。 - E net4
2
作为一种通用解决方案,您可以使用Cow<'static, str>Owned变体将存储一个String - Stefan
这个能行吗?&format!("{}", k as char) - bob
@andy 不,这也会触发一个生命周期错误。 - ljedrz
3个回答

12

只要您不需要从函数返回&str,就可以完全避免使用堆分配,使用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);
}

如果需要更多控制,可以将此与闭包配对使用:

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());
}

或者存储在结构体中以提供一些抽象:

#[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);
    }
}

8

使用 Cow 相当简单:

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()
}

假设ku8(否则你的代码将无法编译),你还可以使用一个常量数组:

const NAMES: [&'static str; 256] = [
    "KEY_0", "KEY_1", //  ...
    " ", "!", "\"", "#", // ...
];

fn const_name(k: u8) -> &'static str {
    NAMES[k as usize]
}

Playground


2

您想以相同的方式处理两个非常不同的东西:编译时已知的字节数组和动态创建的数组。它们不是分配在同一内存区域中,并且寿命也不相同。您可以使用对编译时创建的数组的引用,因为它们与程序一样长时间存在,但是对于动态创建的数组,您不能这样做,因为引用的寿命不会超过其借用的资源。

您最好只使用String

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

相比于弯曲变形,坚持使用&strs在我看来似乎是过早优化。


2
考虑到使用 Cow<'static, str> 的简便性,我认为使用它并不是一种过早的优化。 - Stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接