【问题标题】:Converting a char to &str将 char 转换为 &str
【发布时间】:2017-12-04 08:53:12
【问题描述】:

我有一个match 语句,它返回一个&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()

我尝试先将char 转换为字符串,然后再取其中的一部分:

&(k as char).to_string()[..]

但这给了我终身错误:

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

声明(k as char).to_string() 是一个临时值,这是有道理的,因为我可以告诉to_string() 返回一个克隆。

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

具体问题是我如何将char 变成&str,但更广泛的问题是是否有更好的解决方案,这种情况通常会做什么。

【问题讨论】:

  • 您需要向我们展示更多代码。 k 的类型是什么?它来自哪里?
  • 这肯定是XY问题。除非您返回拥有该字符的东西,否则您不能使本地 char 值的寿命超过函数的范围。您应该考虑返回其他内容,例如枚举。
  • 作为通用解决方案,您可以使用Cow<'static, str>;然后Owned 变体将存储String
  • 这行得通吗? &format!("{}", k as char)
  • @andy 不,这也会触发终身错误。

标签: string reference rust


【解决方案1】:

只要不需要从函数中返回&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);
    }
}

【讨论】:

    【解决方案2】:

    使用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

    【讨论】:

      【解决方案3】:

      这是两个非常不同的东西,您希望以相同的方式处理:编译时已知的字节数组和动态创建的数组。它们没有分配在相同的内存区域中,并且没有相同的生命周期。您可以摆脱对编译时创建的数组的引用,因为它们与程序一样长,但您不能使用动态创建的数组,因为引用不会比它借用的资源持续更长时间。

      只使用Strings 可能会更好:

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

      比扭曲。从我的立场来看,坚持&amp;strs 似乎是过早的优化。

      【讨论】:

      • 鉴于Cow&lt;'static, str&gt; 的使用非常简单,我认为使用它不会为时过早的优化。
      猜你喜欢
      • 1970-01-01
      • 2018-12-20
      • 2017-08-18
      • 1970-01-01
      • 2021-04-29
      • 2013-10-18
      • 2012-01-15
      • 2020-11-25
      • 1970-01-01
      相关资源
      最近更新 更多