字符到字符串转换的所有权问题

bal*_*ve2 2 string char ownership rust borrow-checker

我的失败代码(最小的、可重现的示例):

// my code

fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i].to_string()));
    }
}

// external crate

#[derive(Debug, Clone, Copy, PartialEq)]
struct Text<'a> {
    pub text: &'a str,
}

impl<'a> Text<'a> {
    pub fn new(text: &'a str) -> Self {
        Text {
            text
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器:“借用时临时值下降”。下面有红色波浪线

ARRAY[i].to_string()
Run Code Online (Sandbox Code Playgroud)

我猜是经典的借用检查器问题?

我尝试将类型更改为&strs 而不是chars,一切正常:

// my code
fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [&str; 3] = ["a", "b", "c"]; // CHANGE HERE
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i])); // CHANGE HERE
    }
}
Run Code Online (Sandbox Code Playgroud)

无法弄清楚有什么特别之处char或者转换to_string()使得此代码失败。

rod*_*igo 5

您的失败代码相当于以下代码:

for a in &ARRAY {
    let temp = a.to_string();
    list.push(Text::new(&temp));
    drop(temp);
}
Run Code Online (Sandbox Code Playgroud)

除了临时变量在我的代码中有一个名称(对的调用drop()是多余的,为了清楚起见我添加了它)。

因此,在循环期间创建的所有临时变量都将作为对列表的引用添加,请记住,它Text保存的是引用,而不是字符串本身。但是这些临时对象在创建它们的迭代中无法生存,因此您的列表包含悬空引用。Rust 检测到这一点并且无法编译。

简单的解决方案是修改Text以保存其String本身,而不是引用。如果您无法修改它,那么您的字符串必须位于某个地方,您可以构建Vec<String>并从那里获取引用:

fn test() {
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    let mut tmps: Vec<String> = Vec::new();
    for a in &ARRAY {
        tmps.push(a.to_string());
    }
    let list: Vec<Text> = tmps
        .iter()
        .map(|s| Text::new(s))
        .collect();
}
Run Code Online (Sandbox Code Playgroud)

您的第二个代码之所以有效,是因为现在您的数组包含静态引用&'static str,因此任何地方都没有临时变量。